Node.js 指南(HTTP事务的剖析)

标签: node.js javascript | 发表时间:2018-12-21 16:02 | 作者:博弈
出处:https://segmentfault.com/blogs

HTTP事务的剖析

本指南的目的是让你充分了解Node.js HTTP处理的过程,我们假设你在一般意义上知道HTTP请求的工作方式,无论语言或编程环境如何,我们还假设你对Node.js EventEmittersStreams有点熟悉,如果你对它们不太熟悉,那么值得快速阅读每个API文档。

创建服务器

任何节点Web服务器应用程序在某些时候都必须创建Web服务器对象,这是通过使用 createServer完成的。

  const http = require('http');

const server = http.createServer((request, response) => {
  // magic happens here!
});

传递给 createServer的函数对于针对该服务器发出的每个HTTP请求都会调用一次,因此它被称为请求处理程序,实际上, createServer返回的 Server对象是一个 EventEmitter,我们这里只是创建 server对象的简写,然后稍后添加监听器。

  const server = http.createServer();
server.on('request', (request, response) => {
  // the same kind of magic happens here!
});

当HTTP请求命中服务器时,node使用一些方便的对象调用请求处理函数来处理事务、 requestresponse,我们很快就会讲到。

为了实际处理请求,需要在 server对象上调用 listen方法,在大多数情况下,你需要传递给 listen的是你希望服务器监听的端口号,还有一些其他选项,请参阅API参考。

方法、URL和Headers

处理请求时,你可能要做的第一件事就是查看方法和URL,以便采取适当的措施,Node通过将方便的属性放在 request对象上来使这相对轻松。

  const { method, url } = request;
注意: request对象是 IncomingMessage的一个实例。

这里的 method将始终是普通的HTTP方法/动作, url是没有服务器、协议或端口的完整URL,对于典型的URL,这意味着包括第三个正斜杠后的所有内容。

Headers也不远,它们在自己的 request对象中,被称为 headers

  const { headers } = request;
const userAgent = headers['user-agent'];

这里需要注意的是,无论客户端实际发送它们的方式如何,所有headers都仅以小写字母表示,这简化了为任何目的解析headers的任务。

如果重复某些headers,则它们的值将被覆盖或以逗号分隔的字符串连接在一起,具体取决于header,在某些情况下,这可能会有问题,因此 rawHeaders也可用。

请求体

收到 POSTPUT请求时,请求体可能对你的应用程序很重要,获取body数据比访问请求headers更复杂一点,传递给处理程序的 request对象实现了 ReadableStream接口,就像任何其他流一样,可以在其他地方监听或传输此流,我们可以通过监听流的 'data''end'事件来直接从流中获取数据。

每个 'data'事件中发出的块是一个 Buffer,如果你知道它将是字符串数据,那么最好的方法是在数组中收集数据,然后在 'end',连接并对其进行字符串化。

  let body = [];
request.on('data', (chunk) => {
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
  // at this point, `body` has the entire request body stored in it as a string
});
注意:这看起来有点单调乏味,而且在很多情况下确实如此,幸运的是,在 npm上有像 concat-streambody这样的模块可以帮助隐藏一些逻辑,在走这条路之前,要很好地了解正在发生的事情,这就是为什么你在这里!

关于错误的简单介绍

由于 request对象是一个 ReadableStream,它也是一个 EventEmitter,发生错误时的行为与此类似。

request流中的错误通过在流上发出 'error'事件来呈现,如果你没有该事件的侦听器,则会抛出错误,这可能会导致Node.js程序崩溃。因此,你应该在请求流上添加 'error'侦听器,即使你只是记录它并继续前进(虽然最好发送某种HTTP错误响应,稍后会详细介绍)。

  request.on('error', (err) => {
  // This prints the error message and stack trace to `stderr`.
  console.error(err.stack);
});

还有其他方法可以处理这些错误,例如其他抽象和工具,但始终要注意错误可能并且确实会发生,并且你将不得不处理它们。

到目前为止我们已经得到了什么

此时,我们已经介绍了如何创建服务器,并从请求中获取方法、URL、headers和body,当我们将它们放在一起时,它可能看起来像这样:

  const http = require('http');

http.createServer((request, response) => {
  const { headers, method, url } = request;
  let body = [];
  request.on('error', (err) => {
    console.error(err);
  }).on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString();
    // At this point, we have the headers, method, url and body, and can now
    // do whatever we need to in order to respond to this request.
  });
}).listen(8080); // Activates this server, listening on port 8080.

如果我们运行此示例,我们将能够接收请求,但不会响应它们,实际上,如果你在Web浏览器中请求此示例,则你的请求将超时,因为没有任何内容被发送回客户端。

到目前为止,我们还没有涉及响应对象,它是 ServerResponse的一个实例,它是一个 WritableStream,它包含许多用于将数据发送回客户端的有用方法,接下来我们将介绍。

HTTP状态码

如果不设置它,响应中的HTTP状态码始终为 200,当然,并非每个HTTP响应都保证这一点,并且在某些时候你肯定希望发送不同的状态码,为此,你可以设置 statusCode属性。

  response.statusCode = 404; // Tell the client that the resource wasn't found.

还有其他一些快捷方式,我们很快就会看到。

设置响应Headers

Headers是通过一个名为 setHeader的方便方法设置的。

  response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'bacon');

在响应上设置headers时,大小写对其名称不敏感,如果重复设置标题,则设置的最后一个值是发送的值。

显式发送Header数据

我们已经讨论过的设置headers和状态码的方法假设你正在使用“隐式headers”,这意味着在开始发送body数据之前,你需要依赖node在正确的时间为你发送headers。

如果需要,可以将headers显式写入响应流,为此,有一个名为 writeHead的方法,它将状态码和headers写入流。

  response.writeHead(200, {
  'Content-Type': 'application/json',
  'X-Powered-By': 'bacon'
});

一旦设置了headers(隐式或显式),你就可以开始发送响应数据了。

发送响应体

由于 response对象是 WritableStream,因此将响应体写入客户端只需使用常用的流方法即可。

  response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();

流上的 end函数也可以接收一些可选数据作为流上的最后一位数据发送,因此我们可以如下简化上面的示例。

  response.end('<html><body><h1>Hello, World!</h1></body></html>');
注意:在开始向body写入数据块之前设置状态和headers很重要,这是有道理的,因为headers在HTTP响应中位于body之前。

关于错误的另一件事

response流也可以发出 'error'事件,在某些时候你也必须处理它,所有关于 request流错误的建议仍然适用于此处。

把它放在一起

现在我们已经了解了如何进行HTTP响应,让我们把它们放在一起,在前面的示例的基础上,我们将创建一个服务器,用于发回用户发送给我们的所有数据,我们将使用 JSON.stringify将该数据格式化为JSON。

  const http = require('http');

http.createServer((request, response) => {
  const { headers, method, url } = request;
  let body = [];
  request.on('error', (err) => {
    console.error(err);
  }).on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString();
    // BEGINNING OF NEW STUFF

    response.on('error', (err) => {
      console.error(err);
    });

    response.statusCode = 200;
    response.setHeader('Content-Type', 'application/json');
    // Note: the 2 lines above could be replaced with this next one:
    // response.writeHead(200, {'Content-Type': 'application/json'})

    const responseBody = { headers, method, url, body };

    response.write(JSON.stringify(responseBody));
    response.end();
    // Note: the 2 lines above could be replaced with this next one:
    // response.end(JSON.stringify(responseBody))

    // END OF NEW STUFF
  });
}).listen(8080);

Echo服务器示例

让我们简化前面的示例来进行一个简单的echo服务器,它只是在响应中发送请求中收到的任何数据,我们需要做的就是从请求流中获取数据并将该数据写入响应流,类似于我们之前所做的。

  const http = require('http');

http.createServer((request, response) => {
  let body = [];
  request.on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString();
    response.end(body);
  });
}).listen(8080);

现在让我们调整一下,我们只想在以下条件下发送echo:

  • 请求方法是 POST
  • URL是/echo。

在任何其他情况下,我们只想响应 404

  const http = require('http');

http.createServer((request, response) => {
  if (request.method === 'POST' && request.url === '/echo') {
    let body = [];
    request.on('data', (chunk) => {
      body.push(chunk);
    }).on('end', () => {
      body = Buffer.concat(body).toString();
      response.end(body);
    });
  } else {
    response.statusCode = 404;
    response.end();
  }
}).listen(8080);
注意:通过这种方式检查URL,我们正在做一种“路由”的形式,其他形式的路由可以像 switch语句一样简单,也可以像 express这样的整个框架一样复杂,如果你正在寻找可以进行路由的东西,请尝试使用 router

现在让我们来简化一下吧,请记住, request对象是 ReadableStreamresponse对象是 WritableStream,这意味着我们可以使用 pipe将数据从一个引导到另一个,这正是我们想要的echo服务器!

  const http = require('http');

http.createServer((request, response) => {
  if (request.method === 'POST' && request.url === '/echo') {
    request.pipe(response);
  } else {
    response.statusCode = 404;
    response.end();
  }
}).listen(8080);

我们还没有完成,正如本指南中多次提到的,错误可以而且确实会发生,我们需要处理它们。

为了处理请求流上的错误,我们将错误记录到 stderr并发送 400状态码以指示 Bad Request,但是,在实际应用程序中,我们需要检查错误以确定正确的状态码和消息是什么,与通常的错误一样,你应该查阅错误文档。

在响应中,我们只是将错误记录到 stderr

  const http = require('http');

http.createServer((request, response) => {
  request.on('error', (err) => {
    console.error(err);
    response.statusCode = 400;
    response.end();
  });
  response.on('error', (err) => {
    console.error(err);
  });
  if (request.method === 'POST' && request.url === '/echo') {
    request.pipe(response);
  } else {
    response.statusCode = 404;
    response.end();
  }
}).listen(8080);

我们现在已经介绍了处理HTTP请求的大部分基础知识,此时,你应该能够:

  • 使用请求处理程序函数实例化HTTP服务器,并让它侦听端口。
  • request对象中获取headers、URL、方法和body数据。
  • 根据 request对象中的URL和/或其他数据做出路由决策。
  • 通过 response对象发送headers、HTTP状态码和body数据。
  • request对象和 response对象管道数据。
  • 处理 requestresponse流中的流错误。

从这些基础知识中,可以构建用于许多典型用例的Node.js HTTP服务器,这些API提供了许多其他功能,因此请务必阅读有关 EventEmittersStreamsHTTP的API文档。


上一篇:Node.js中的定时器

相关 [node js http] 推荐:

node js 断点调试

- - Web前端 - ITeye博客
大部分基于 Node.js 的应用都是运行在浏览器中的,. 例如强大的调试工具 node-inspector. node-inspector 是一个完全基于 Node.js 的开源在线调试工具,提供了强大的调试功能和友好. 的用户界面,它的使用方法十分简便. 首先,使用 npm install -g node-inspector 命令安装 node-inspector,然后在终.

什么是Node?

- We_Get - 博客园新闻频道
译者按:前不久Oreilly出了一本小册子“What is Node?”,扼要的讲解了Node的身世和所适用的场景,作者文笔轻松流畅、内容充实,是非常难得的学习资料.   译文全文:http://jayli.github.com/whatisnode/index.html.   作者:Brett McLaughlin ,原文:What is Node?.

Node入门

- - CSDN博客编程语言推荐文章
作者:  Manuel Kiessling. 翻译:  goddyzhao &  GrayZhang &  MondayChen. 本书致力于教会你如何用Node.js来开发应用,过程中会传授你所有所需的“高级”JavaScript知识. 本书绝不是一本“Hello World”的教程. 你正在阅读的已经是本书的最终版.

浅析Hadoop Secondary NameNode,CheckPoint Node,Backup Node

- - CSDN博客云计算推荐文章
Hadoop SecondaryNameNode并不是Hadoop 第二个NameNode,它不提供NameNode服务,而仅仅是NameNode的一个工具. 这个工具帮助NameNode管理Metadata数据. NameNode的HDFS文件信息(即Metadata)记录在内存中,client的文件写操作直接修改内存中的Metadata,同时也会记录到硬盘的Edits文件,这是一个Log文件.

[译]什么是Node?

- blacktulip - Taobao UED Team
译者按:前不久Oreilly出了一本小册子“What is Node?”,扼要的讲解了Node的身世和所适用的场景,作者文笔轻松流畅、内容充实,是非常难得的学习资料. 译文全文:http://jayli.github.com/whatisnode/index.html. 作者:Brett McLaughlin ,原文:What is Node?.

用node作桌面开发

- InterMa - CNode社区
node的定位是,server-side javascript. 但程序员最爱做的事,就是把一个东西用在不该用的地方. 那么,可以把node用在桌面开发上吗. 把Javascript用在桌面开发上,早有先例,比如GTK+的gjs,还有Qt的QML(顺带一提,QML代表着桌面开发的另一个方向,a promising way),GNOME3中,也用javascript作为桌面插件的开发语言.

Vercel 部署 Node 服务

- - 掘金 前端
之前在写 面试常客:HTTP 缓存时,曾经就强缓存和协商缓存写过两个demo,但缓存要在服务端做,只能贴上代码,不能在网页上感受(虽然我贴了gif). 笔者的所有 demo 例子都放在 github page 上,其特点是不需要服务器即可部署静态资源,但它不具备部署服务端应用能力. 最近笔者在了解 CI/CD 方面的知识点,想起了 Vercel,就想着能否将服务端应用架在 vercel 上呢.

WebView JS 交互

- - ITeye博客
WebView加jquery做页面会怎么样呢. // 创建WebView对象. // 把programList添加到js的全局对象window中,. // 这样就可以使用window.programList来获取数据. * 定义js回调java函数. // 绑定键盘的向上,向下按钮事件触发相应的js事件.

無痛安裝 NodeJS 和 Node Framework Express

- Hming - 小惡魔 - 電腦技術 - 工作筆記 - AppleBOY
直接到官網下載 Stable 的版本吧,目前是 node-v0.4.10.tar.gz,也可以先看看 API Document. 安裝 Ububtu 相關套件. 下面會使用最原始的編譯方式,所以必須安裝 g++ 套件,否則下 ./configure 的時候,會吐出來沒有安裝過的套件. 兩種方法:1.用 apt-get install nodejs 2.