咱們已經知道如何使用nodejs搭建一個HTTP服務,今天咱們會詳細的介紹nodejs中的HTTP處理流程,從而對nodejs的HTTP進行深刻的理解。html
使用nodejs建立HTTP服務很簡單,nodejs提供了專門的HTTP模塊,咱們可使用其中的createServer方法來輕鬆建立HTTP服務:node
const http = require('http'); const server = http.createServer((request, response) => { // magic happens here! });
首先createServer方法傳入的是一個callback函數,這個callback函數將會在每次服務端接收到客戶端的請求時調用。因此這個callback函數,也叫作 request handler.git
再看看createServer的返回值,createServer返回的是一個EventEmitter對象。github
以前咱們也介紹過了EventEmitter,它能夠發送和接收事件,因此咱們可使用on來監聽客戶端的事件。web
上面的代碼至關於:express
const server = http.createServer(); server.on('request', (request, response) => { // the same kind of magic happens here! });
當發送request事件的時候,就會觸發後面的handler method,並傳入request和response參數。咱們能夠在這個handler中編寫業務邏輯。json
固然,爲了讓http server正常運行,咱們還須要加上listen方法,來綁定ip和端口,以最終啓動服務。app
const hostname = '127.0.0.1' const port = 3000 server.listen(port, hostname, () => { console.log(`please visit http://${hostname}:${port}/`) })
上面的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屬性。koa
怎麼從request中拿到這些屬性呢?對的,咱們可使用ES6中解構賦值:
const { method, url } = request; const { headers } = request; const userAgent = headers['user-agent'];
其中request的headers是一個IncomingHttpHeaders,它繼承自NodeJS.Dict。
從源碼能夠看出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是一個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的博客
歡迎關注個人公衆號:「程序那些事」最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!