简介

我们已经知道如何使用nodejs搭建一个HTTP服务,今天我们会详细的介绍nodejs中的HTTP处理流程,从而对nodejs的HTTP进行深入的理解。

使用nodejs创建HTTP服务

使用nodejs创建HTTP服务很简单,nodejs提供了专门的HTTP模块,我们可以使用其中的createServer方法来轻松创建HTTP服务:

  1. const http = require('http');
  2. const server = http.createServer((request, response) => {
  3. // magic happens here!
  4. });

首先createServer方法传入的是一个callback函数,这个callback函数将会在每次服务端接收到客户端的请求时调用。所以这个callback函数,也叫做 request handler.

再看看createServer的返回值,createServer返回的是一个EventEmitter对象。

之前我们也介绍过了EventEmitter,它可以发送和接收事件,所以我们可以使用on来监听客户端的事件。

上面的代码相当于:

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

当发送request事件的时候,就会触发后面的handler method,并传入request和response参数。我们可以在这个handler中编写业务逻辑。

当然,为了让http server正常运行,我们还需要加上listen方法,来绑定ip和端口,以最终启动服务。

  1. const hostname = '127.0.0.1'
  2. const port = 3000
  3. server.listen(port, hostname, () => {
  4. console.log(`please visit http://${hostname}:${port}/`)
  5. })

解构request

上面的request参数实际上是一个http.IncomingMessage对象,我们看下这个对象的定义:

  1. class IncomingMessage extends stream.Readable {
  2. constructor(socket: Socket);
  3. aborted: boolean;
  4. httpVersion: string;
  5. httpVersionMajor: number;
  6. httpVersionMinor: number;
  7. complete: boolean;
  8. /**
  9. * @deprecate Use `socket` instead.
  10. */
  11. connection: Socket;
  12. socket: Socket;
  13. headers: IncomingHttpHeaders;
  14. rawHeaders: string[];
  15. trailers: NodeJS.Dict<string>;
  16. rawTrailers: string[];
  17. setTimeout(msecs: number, callback?: () => void): this;
  18. /**
  19. * Only valid for request obtained from http.Server.
  20. */
  21. method?: string;
  22. /**
  23. * Only valid for request obtained from http.Server.
  24. */
  25. url?: string;
  26. /**
  27. * Only valid for response obtained from http.ClientRequest.
  28. */
  29. statusCode?: number;
  30. /**
  31. * Only valid for response obtained from http.ClientRequest.
  32. */
  33. statusMessage?: string;
  34. destroy(error?: Error): void;
  35. }

通常我们需要用到request中的method,url和headers属性。

怎么从request中拿到这些属性呢?对的,我们可以使用ES6中解构赋值:

  1. const { method, url } = request;
  2. const { headers } = request;
  3. const userAgent = headers['user-agent'];

其中request的headers是一个IncomingHttpHeaders,它继承自NodeJS.Dict。

处理Request Body

从源码可以看出request是一个Stream对象,对于stream对象来说,我们如果想要获取其请求body的话,就不像获取静态的method和url那么简单了。

我们通过监听Request的data和end事件来处理body。

  1. let body = [];
  2. request.on('data', (chunk) => {
  3. body.push(chunk);
  4. }).on('end', () => {
  5. body = Buffer.concat(body).toString();
  6. // at this point, `body` has the entire request body stored in it as a string
  7. });

因为每次data事件,接收到的chunk实际上是一个Buffer对象。我们将这些buffer对象保存起来,最后使用Buffer.concat来对其进行合并,最终得到最后的结果。

直接使用nodejs来处理body看起来有点复杂,幸运的是大部分的nodejs web框架,比如koa和express都简化了body的处理。

处理异常

异常处理是通过监听request的error事件来实现的。

如果你在程序中并没有捕获error的处理事件,那么error将会抛出并终止你的nodejs程序,所以我们一定要捕获这个error事件。

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

解构response

response是一个http.ServerResponse类:

  1. class ServerResponse extends OutgoingMessage {
  2. statusCode: number;
  3. statusMessage: string;
  4. constructor(req: IncomingMessage);
  5. assignSocket(socket: Socket): void;
  6. detachSocket(socket: Socket): void;
  7. // https://github.com/nodejs/node/blob/master/test/parallel/test-http-write-callbacks.js#L53
  8. // no args in writeContinue callback
  9. writeContinue(callback?: () => void): void;
  10. writeHead(statusCode: number, reasonPhrase?: string, headers?: OutgoingHttpHeaders): this;
  11. writeHead(statusCode: number, headers?: OutgoingHttpHeaders): this;
  12. writeProcessing(): void;
  13. }

对于response来说,我们主要关注的是statusCode:

  1. response.statusCode = 404;

Response Headers:

response提供了setHeader方法来设置相应的header值。

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

还有一个更加直接的同时写入head和status code:

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

最后,我们需要写入response body,因为response是一个WritableStream,所以我们可以多次写入,最后以end方法结束:

  1. response.write('<html>');
  2. response.write('<body>');
  3. response.write('<h1>Hello, World!</h1>');
  4. response.write('</body>');
  5. response.write('</html>');
  6. response.end();

或者我们可以用一个end来替换:

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

综上,我们的代码是这样的:

  1. const http = require('http');
  2. http.createServer((request, response) => {
  3. const { headers, method, url } = request;
  4. let body = [];
  5. request.on('error', (err) => {
  6. console.error(err);
  7. }).on('data', (chunk) => {
  8. body.push(chunk);
  9. }).on('end', () => {
  10. body = Buffer.concat(body).toString();
  11. // BEGINNING OF NEW STUFF
  12. response.on('error', (err) => {
  13. console.error(err);
  14. });
  15. response.statusCode = 200;
  16. response.setHeader('Content-Type', 'application/json');
  17. // Note: the 2 lines above could be replaced with this next one:
  18. // response.writeHead(200, {'Content-Type': 'application/json'})
  19. const responseBody = { headers, method, url, body };
  20. response.write(JSON.stringify(responseBody));
  21. response.end();
  22. // Note: the 2 lines above could be replaced with this next one:
  23. // response.end(JSON.stringify(responseBody))
  24. // END OF NEW STUFF
  25. });
  26. }).listen(8080);

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/nodejs-http-in-depth/

本文来源:flydean的博客

欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

深入理解nodejs的HTTP处理流程的更多相关文章

  1. 从原理上理解NodeJS的适用场景

    NodeJS是近年来比较火的服务端JS平台,这一方面得益于其在后端处理高并发的卓越性能,另一方面在nodeJS平台上的npm.grunt.express等强大的代码与项目管理应用崛起,几乎重新定义了前 ...

  2. 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...

  3. [NodeJs系列][译]理解NodeJs中的Event Loop、Timers以及process.nextTick()

    译者注: 为什么要翻译?其实在翻译这篇文章前,笔者有Google了一下中文翻译,看的不是很明白,所以才有自己翻译的打算,当然能力有限,文中或有错漏,欢迎指正. 文末会有几个小问题,大家不妨一起思考一下 ...

  4. 深入理解nodejs中的异步编程

    目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...

  5. 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本 ...

  6. 理解nodejs的module模块儿

    module 在 Node.js 模块系统中,每个文件都视为独立的模块,node在运行某个模块儿时会生成一个module对象 Module { id: '.', exports: 2, parent: ...

  7. 理解Nodejs的Event Loop

    Node的“event loop”主要是用来处理高输出量的.这很神奇,这也是为什么node可以在单线程的情况下同时处理很多的后台操作.本文就会集中讲述event loop是怎么运行的,这样你可以可以使 ...

  8. Nodejs中使用异步流程控制Async

    首先,我们都知道,Node基于事件驱动的异步I/O架构,所谓异步就是非阻塞,说白了就是一个事件执行了,我不必等待它执行完成后我才能执行下一个事件.所以在Node环境中的模块基本都是异步的,上一篇说到我 ...

  9. 77.深入理解nodejs中Express的中间件

    转自:https://blog.csdn.net/huang100qi/article/details/80220012 Express是一个基于Node.js平台的web应用开发框架,在Node.j ...

随机推荐

  1. vue-cli3 创建项目路由缺失问题

    1.在项目中新建一个router.js router.js import Vue from 'vue' import Router from 'vue-router' import Home from ...

  2. Linux下docker中安装宝塔面板教程

    本人云服务器,装的cent os7.6,在cent os7.6已装了docker,没装的可以借鉴 https://www.cnblogs.com/xiaoyige/p/12673076.html 1. ...

  3. 【Java】集合综合案例 - 播放器管理

    集合综合案例 文章目录 集合综合案例 需求分析 项目演示 详细设计 代码实现 歌曲类 播放器类 播放列表类 测试 参考资料 播放器管理 需求分析 项目演示 详细设计 代码实现 重新搞一波 复习巩固 简 ...

  4. 【Linux】实现端口转发的rinetd

    Linux下端口转发一般都使用iptables来实现,使用iptables可以很容易将TCP和UDP端口从防火墙转发到内部主机上.但是如果需要将流量从专用地址转发到不在您当前网络上的机器上,可尝试另一 ...

  5. Unsafe Filedownload - Pikachu

    概述: 文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,便会向后台发送一个下载请求,一般这个请求会包含一个需要下载的文件名称,后台在收到请求后会开始执行下载代码,将该文件名对应的文件r ...

  6. File Inclusion - Pikachu

    概述: 文件包含,是一个功能.在各种开发语言中都提供了内置的文件包含函数,其可以使开发人员在一个代码文件中直接包含(引入)另外一个代码文件. 比如 在PHP中,提供了: include(),inclu ...

  7. i春秋新春战疫—web—简单的招聘系统

    打开靶机 打开后看到登录界面 利用万能密码,以admin身份登录 登录成功后看到如下界面 在Blank Page界面内发现注入点,抓包 保存在sqlmap目录下test.txt文件夹,使用sqlmap ...

  8. 为什么不建议用var

    看了这个例子估计你就会明白了 var a = 'global'; function test() { if (!a) { var a = 'part'; } console.log(a); } tes ...

  9. commons-lang3相关类实例

    一.ArrayUtils //1.判断两个数组长度是否相等 ArrayUtils.isSameLength(new int[] {1,2,3,4}, new int[] {1,2,3,4});//tr ...

  10. MAX232数据方向

    在调试一个新板子的时候,串口调试从来都是放在前面的,而由于是一个新板子,电路图也是新的,因此有时候不知道串口线结对了没有,这个时候可能会对照PCB和原理图去看一下,但有时候看的人会很迷糊,因为不同的人 ...