关于http:深入理解nodejs的HTTP处理流程

31次阅读

共计 4559 个字符,预计需要花费 12 分钟才能阅读完成。

简介

咱们曾经晓得如何应用 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<string>;
        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('<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>');

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

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 的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

正文完
 0