深入理解nodejs的HTTP处理流程
文章目錄
- 簡介
- 使用nodejs創(chuàng)建HTTP服務(wù)
- 解構(gòu)request
- 處理Request Body
- 處理異常
- 解構(gòu)response
簡介
我們已經(jīng)知道如何使用nodejs搭建一個HTTP服務(wù),今天我們會詳細(xì)的介紹nodejs中的HTTP處理流程,從而對nodejs的HTTP進(jìn)行深入的理解。
使用nodejs創(chuàng)建HTTP服務(wù)
使用nodejs創(chuàng)建HTTP服務(wù)很簡單,nodejs提供了專門的HTTP模塊,我們可以使用其中的createServer方法來輕松創(chuàng)建HTTP服務(wù):
const http = require('http');const server = http.createServer((request, response) => {// magic happens here! });首先createServer方法傳入的是一個callback函數(shù),這個callback函數(shù)將會在每次服務(wù)端接收到客戶端的請求時調(diào)用。所以這個callback函數(shù),也叫做 request handler.
再看看createServer的返回值,createServer返回的是一個EventEmitter對象。
之前我們也介紹過了EventEmitter,它可以發(fā)送和接收事件,所以我們可以使用on來監(jiān)聽客戶端的事件。
上面的代碼相當(dāng)于:
const server = http.createServer(); server.on('request', (request, response) => {// the same kind of magic happens here! });當(dāng)發(fā)送request事件的時候,就會觸發(fā)后面的handler method,并傳入request和response參數(shù)。我們可以在這個handler中編寫業(yè)務(wù)邏輯。
當(dāng)然,為了讓http server正常運行,我們還需要加上listen方法,來綁定ip和端口,以最終啟動服務(wù)。
const hostname = '127.0.0.1' const port = 3000server.listen(port, hostname, () => {console.log(`please visit http://${hostname}:${port}/`) })解構(gòu)request
上面的request參數(shù)實際上是一個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中解構(gòu)賦值:
const { method, url } = request;const { headers } = request; const userAgent = headers['user-agent'];其中request的headers是一個IncomingHttpHeaders,它繼承自NodeJS.Dict。
處理Request Body
從源碼可以看出request是一個Stream對象,對于stream對象來說,我們?nèi)绻胍@取其請求body的話,就不像獲取靜態(tài)的method和url那么簡單了。
我們通過監(jiān)聽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來對其進(jìn)行合并,最終得到最后的結(jié)果。
直接使用nodejs來處理body看起來有點復(fù)雜,幸運的是大部分的nodejs web框架,比如koa和express都簡化了body的處理。
處理異常
異常處理是通過監(jiān)聽request的error事件來實現(xiàn)的。
如果你在程序中并沒有捕獲error的處理事件,那么error將會拋出并終止你的nodejs程序,所以我們一定要捕獲這個error事件。
request.on('error', (err) => {// This prints the error message and stack trace to `stderr`.console.error(err.stack); });解構(gòu)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 callbackwriteContinue(callback?: () => void): void;writeHead(statusCode: number, reasonPhrase?: string, headers?: OutgoingHttpHeaders): this;writeHead(statusCode: number, headers?: OutgoingHttpHeaders): this;writeProcessing(): void;}對于response來說,我們主要關(guān)注的是statusCode:
response.statusCode = 404;Response Headers:
response提供了setHeader方法來設(shè)置相應(yīng)的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方法結(jié)束:
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 STUFFresponse.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的博客
歡迎關(guān)注我的公眾號:「程序那些事」最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的深入理解nodejs的HTTP处理流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不要在nodejs中阻塞event lo
- 下一篇: 使用V8和node轻松profile分析