简介

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

使用nodejs创建HTTP服务

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

const http = require('http');

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

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

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

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

上面的代码相当于:

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

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

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

const hostname = '127.0.0.1'
const port = 3000

server.listen(port, hostname, () => {
  console.log(`please visit http://${hostname}:${port}/`)
}) 

解构request

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

 class IncomingMessage extends stream.Readable {
        constructor(socket: Socket);

        aborted: boolean;
        httpVersion: string;
        httpVersionMajor: number;
        httpVersionMinor: number;
        complete: boolean;
        /**
         * @deprecate Use `socket` instead.
         */
        connection: Socket;
        socket: Socket;
        headers: IncomingHttpHeaders;
        rawHeaders: string[];
        trailers: NodeJS.Dict;
        rawTrailers: string[];
        setTimeout(msecs: number, callback?: () => void): this;
        /**
         * Only valid for request obtained from http.Server.
         */
        method?: string;
        /**
         * Only valid for request obtained from http.Server.
         */
        url?: string;
        /**
         * Only valid for response obtained from http.ClientRequest.
         */
        statusCode?: number;
        /**
         * Only valid for response obtained from http.ClientRequest.
         */
        statusMessage?: string;
        destroy(error?: Error): void;
    } 

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

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

const { method, url } = request;

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

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

处理Request Body

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

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

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
}); 

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

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

处理异常

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

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

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

解构response

response是一个http.ServerResponse类:

 class ServerResponse extends OutgoingMessage {
        statusCode: number;
        statusMessage: string;

        constructor(req: IncomingMessage);

        assignSocket(socket: Socket): void;
        detachSocket(socket: Socket): void;
        // https://github.com/nodejs/node/blob/master/test/parallel/test-http-write-callbacks.js#L53
        // no args in writeContinue callback
        writeContinue(callback?: () => void): void;
        writeHead(statusCode: number, reasonPhrase?: string, headers?: OutgoingHttpHeaders): this;
        writeHead(statusCode: number, headers?: OutgoingHttpHeaders): this;
        writeProcessing(): void;
    } 

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

response.statusCode = 404; 

Response Headers:

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

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

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

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

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

response.write('');
response.write('');
response.write('
	

Hello, World!

'); response.write(''); response.write(''); response.end();

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

response.end('
	

Hello, World!

');

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

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); 

本文作者:flydean程序那些事

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

本文来源:flydean的博客

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

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

  1. Node使用koa2实现一个简单JWT鉴权的方法

    JWT 简介什么是 JWT全称 JSON Web Token , 是目前最流行的跨域认证解决方案。基本的实现是服务端......

  2. JS实现公告上线滚动效果

    本文实例为大家分享了JS实现公告上线滚动效果的具体代码,供大家参考,具体内容如下实现的效果如下,新闻公告上下滚动。代......

  3. 在使用内模块的时候需要先将所需的内置模块进行引入、OS模块在nodejs中OS模块提供了与操作系统相关的属性和方法/......

  4. nodejs的调试debug

    目录简介开启nodejs的调试调试的安全性使用WebStorm进行nodejs调试使用Chrome devTools......

  5. 在nodejs中创建cluster

    目录简介cluster集群cluster详解cluster中的eventcluster中的方法cluster中的属性......

  6. 深入理解nodejs的HTTP处理流程

    目录简介使用nodejs创建HTTP服务解构request处理Request Body处理异常解构response简......

  7. 在nodejs中创建child process

    目录简介child process异步创建进程同步创建进程在nodejs中创建child process简介node......

  8. Node.js 安全指南

    当项目周期快结束时,开发人员会越来越关注应用的“安全性”问题。一个安全的应用程序并不是一种奢侈,而是必要的。你应该在......

  9. nodejs中的文件系统

    、目录简介nodejs中的文件系统模块Promise版本的fs文件描述符fs.stat文件状态信息fs的文件读写fs......

  10. three.js cannon.js物理引擎之制作拥有物理特性的汽车

    今天郭先生说一说使用cannon.js的车辆辅助类让我们的汽车模型拥有物理特性。效果图如下,在线案例请点击博客原文。......

随机推荐

  1. Java语法糖详解

    语法糖语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明......

  2. 如何使用 JavaScript 操作浏览器历史记录 API

    History 是 window 对象中的一个 JavaScript 对象,它包含了关于浏览器会话历史的详细信息。你......

  3. docker+mysql集群+读写分离+mycat管理+垂直分库+负载均衡

    依然如此,只要大家跟着我的步骤一步步来,99.99999%是可以测试成功的centos6.8已不再维护,可能很多人的......

  4. vue 递归组件的简单使用示例

    前言递归 相信很多同学已经不陌生了,算法中我们经常用递归来解决问题。比如经典的:从一个全为数字的数组中找出其中相加能......

  5. php中yum命令用法详解

    在php中关于yum命令还是比较多的,主要是因为在配置linux环境,linux中安装、卸载等各种操作中,因此,掌握......

  6. C#9.0:Init相关总结

    背景在以前的C#版本里面,如果需要定义一个不可修改的的类型的做法一般是:声明为readonly,并设置为只包含get......

  7. Vue3(三)CND + ES6的import + 工程化的目录结构

    突发奇想这几天整理了一下vue的几种使用方式,对比之后发现有很多相似之处,那么是不是可以混合使用呢?比如这样:vue......

  8. java中Map、Set、List的简单使用教程(快速入门)

    Map、Set、ListList的常用方法1、创建List list = new ArrayList<>......

  9. Java Object类 和 String类 常见问答

    Java常见对象 Object类 和 String类 常见问答 6k字+总结写在最前面这个项目是从20年末就立好的 ......

  10. HTML5与CSS3知识点总结

    HTML常用标签总结 手摸手带你学CSS 好好学习,天天向上本文已收录至我的Github仓库DayDayUP:git......