简介

咱们曾经晓得如何应用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 = 3000server.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的博客

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