一,開篇分析
首先“Http”這個(gè)概念大家應(yīng)該比較熟悉了,它不是基于特定語言的,是一個(gè)通用的應(yīng)用層協(xié)議,不同語言有不同的實(shí)現(xiàn)細(xì)節(jié),但是萬變不離其宗,思想是相同的,
NodeJS作為一個(gè)宿主運(yùn)行環(huán)境,以JavaScript為宿主語言,它也有自己實(shí)現(xiàn)的一套標(biāo)準(zhǔn),這篇文章我們就一起來學(xué)習(xí)一下 “Http模塊” 。但是作為前提來說,
希望大家可以先閱讀一下官網(wǎng)提供的api,有一個(gè)前置了解,這樣就方便多了,以下是Http部分的api概覽:
HTTP
http.STATUS_CODES
http.createServer([requestListener])
http.createClient([port], [host])
Class: http.Server
事件 : 'request'
事件: 'connection'
事件: 'close'
Event: 'checkContinue'
事件: 'connect'
Event: 'upgrade'
Event: 'clientError'
server.listen(port, [hostname], [backlog], [callback])
server.listen(path, [callback])
server.listen(handle, [callback])
server.close([callback])
server.maxHeadersCount
server.setTimeout(msecs, callback)
server.timeout
Class: http.ServerResponse
事件: 'close'
response.writeContinue()
response.writeHead(statusCode, [reasonPhrase], [headers])
response.setTimeout(msecs, callback)
response.statusCode
response.setHeader(name, value)
response.headersSent
response.sendDate
response.getHeader(name)
response.removeHeader(name)
response.write(chunk, [encoding])
response.addTrailers(headers)
response.end([data], [encoding])
http.request(options, callback)
http.get(options, callback)
Class: http.Agent
new Agent([options])
agent.maxSockets
agent.maxFreeSockets
agent.sockets
agent.freeSockets
agent.requests
agent.destroy()
agent.getName(options)
http.globalAgent
Class: http.ClientRequest
Event 'response'
Event: 'socket'
事件: 'connect'
Event: 'upgrade'
Event: 'continue'
request.write(chunk, [encoding])
request.end([data], [encoding])
request.abort()
request.setTimeout(timeout, [callback])
request.setNoDelay([noDelay])
request.setSocketKeepAlive([enable], [initialDelay])
http.IncomingMessage
事件: 'close'
message.httpVersion
message.headers
message.rawHeaders
message.trailers
message.rawTrailers
message.setTimeout(msecs, callback)
message.method
message.url
message.statusCode
message.socket
讓我們先從一個(gè)簡單例子開始,創(chuàng)建一個(gè)叫server.js的文件,并寫入以下代碼:
var http = require('http') ;
var server = http.createServer(function(req,res){
res.writeHeader(200,{
'Content-Type' : 'text/plain;charset=utf-8' // 添加charset=utf-8
}) ;
res.end("Hello,大熊!") ;
}) ;
server.listen(8888) ;
console.log("http server running on port 8888 ...") ;
(node server.js)以下是運(yùn)行結(jié)果:
二,細(xì)節(jié)分析實(shí)例
具體看一下這個(gè)小例子:
(1行):通過"require"引入NodeJS自帶的"http"模塊,并且把它賦值給http變量。
(2行):調(diào)用http模塊提供的函數(shù):"createServer" 。這個(gè)函數(shù)會(huì)返回一個(gè)新的web服務(wù)器對(duì)象。
參數(shù) "requestListener" 是一個(gè)函數(shù),它將會(huì)自動(dòng)加入到 "request" 事件的監(jiān)聽隊(duì)列。
當(dāng)一個(gè)request到來時(shí),Event-Loop會(huì)將這個(gè)Listener回調(diào)函數(shù)放入執(zhí)行隊(duì)列, node中所有的代碼都是一個(gè)一個(gè)從執(zhí)行隊(duì)列中拿出來執(zhí)行的。
這些執(zhí)行都是在工作線程上(Event Loop本身可以認(rèn)為在一個(gè)獨(dú)立的線程中,我們一般不提這個(gè)線程,而將node稱呼為一個(gè)單線程的執(zhí)行環(huán)境),
所有的回調(diào)都是在一個(gè)工作線程上運(yùn)行。
我們?cè)谠賮砜匆幌?requestListener"這個(gè)回調(diào)函數(shù),它提供了兩個(gè)參數(shù)(request,response),
每次收到一個(gè)請(qǐng)求時(shí)觸發(fā)。注意每個(gè)連接又可能有多個(gè)請(qǐng)求(在keep-alive的連接中)。
"request"是http.IncomingMessage的一個(gè)實(shí)例。"response"是http.ServerResponse的一個(gè)實(shí)例。
一個(gè)http request對(duì)象是可讀流,而http response對(duì)象則是可寫流。
一個(gè)"IncomingMessage"對(duì)象是由http.Server或http.ClientRequest創(chuàng)建的,
并作為第一參數(shù)分別傳遞給"request"和"response"事件。
它也可以被用來訪問應(yīng)答的狀態(tài),頭文件和數(shù)據(jù)。
它實(shí)現(xiàn)了 "Stream" 接口以及以下額外的事件,方法和屬性。(具體參考api)。
(3行):“writeHeader”,使用 "response.writeHead()" 函數(shù)發(fā)送一個(gè)Http狀態(tài)200和Http頭的內(nèi)容類型(content-type)。
向請(qǐng)求回復(fù)響應(yīng)頭。"statusCode"是一個(gè)三位是的HTTP狀態(tài)碼,例如 404 。最后一個(gè)參數(shù),"headers",是響應(yīng)頭的內(nèi)容。
舉個(gè)栗子:
var body = 'hello world' ;
response.writeHead(200, {
'Content-Length': body.length,
'Content-Type': 'text/plain'
}) ;
注意:Content-Length 是以字節(jié)(byte)計(jì)算,而不是以字符(character)計(jì)算。
之前的例子原因是字符串 “Hello World !” 只包含了單字節(jié)的字符。
如果body包含了多字節(jié)編碼的字符,就應(yīng)當(dāng)使用Buffer.byteLength()來確定在多字節(jié)字符編碼情況下字符串的字節(jié)數(shù)。
需要進(jìn)一步說明的是Node不檢查Content-Lenth屬性和已傳輸?shù)腷ody長度是否吻合。
statusCode是一個(gè)三位是的HTTP狀態(tài)碼, 例如:"404" 。這里要說的是 "http.STATUS_CODES" ,全部標(biāo)準(zhǔn)"Http"響應(yīng)狀態(tài)碼的集合和簡短描述都在里面。
如下是源碼參考:
var STATUS_CODES = exports.STATUS_CODES = {
100 : 'Continue',
101 : 'Switching Protocols',
102 : 'Processing', // RFC 2518, obsoleted by RFC 4918
200 : 'OK',
201 : 'Created',
202 : 'Accepted',
203 : 'Non-Authoritative Information',
204 : 'No Content',
205 : 'Reset Content',
206 : 'Partial Content',
207 : 'Multi-Status', // RFC 4918
300 : 'Multiple Choices',
301 : 'Moved Permanently',
302 : 'Moved Temporarily',
303 : 'See Other',
304 : 'Not Modified',
305 : 'Use Proxy',
307 : 'Temporary Redirect',
400 : 'Bad Request',
401 : 'Unauthorized',
402 : 'Payment Required',
403 : 'Forbidden',
404 : 'Not Found',
405 : 'Method Not Allowed',
406 : 'Not Acceptable',
407 : 'Proxy Authentication Required',
408 : 'Request Time-out',
409 : 'Conflict',
410 : 'Gone',
411 : 'Length Required',
412 : 'Precondition Failed',
413 : 'Request Entity Too Large',
414 : 'Request-URI Too Large',
415 : 'Unsupported Media Type',
416 : 'Requested Range Not Satisfiable',
417 : 'Expectation Failed',
418 : 'I/'m a teapot', // RFC 2324
422 : 'Unprocessable Entity', // RFC 4918
423 : 'Locked', // RFC 4918
424 : 'Failed Dependency', // RFC 4918
425 : 'Unordered Collection', // RFC 4918
426 : 'Upgrade Required', // RFC 2817
500 : 'Internal Server Error',
501 : 'Not Implemented',
502 : 'Bad Gateway',
503 : 'Service Unavailable',
504 : 'Gateway Time-out',
505 : 'HTTP Version not supported',
506 : 'Variant Also Negotiates', // RFC 2295
507 : 'Insufficient Storage', // RFC 4918
509 : 'Bandwidth Limit Exceeded',
510 : 'Not Extended' // RFC 2774
};
節(jié)選自,Nodejs源碼 ”http.js“ 143行開始。
其實(shí)從客戶端應(yīng)答結(jié)果也不難看出:
(6行):”response.end“------當(dāng)所有的響應(yīng)報(bào)頭和報(bào)文被發(fā)送完成時(shí)這個(gè)方法將信號(hào)發(fā)送給服務(wù)器。服務(wù)器會(huì)認(rèn)為這個(gè)消息完成了。
每次響應(yīng)完成之后必須調(diào)用該方法。如果指定了參數(shù) “data” ,就相當(dāng)于先調(diào)用 “response.write(data, encoding) ” 之后再調(diào)用 “response.end()” 。
(8行):”server.listen(8888)“ ------ 服務(wù)器用指定的句柄接受連接,綁定在特定的端口。
以上就是一個(gè)比較詳細(xì)的分析過程,希望有助于加深理解,代碼雖然不多,但是重在理解一些細(xì)節(jié)機(jī)制,以便日后高效的開發(fā)NodeJS應(yīng)用。
三,實(shí)例
除了可以使用"request"對(duì)象訪問請(qǐng)求頭數(shù)據(jù)外,還能把"request"對(duì)象當(dāng)作一個(gè)只讀數(shù)據(jù)流來訪問請(qǐng)求體數(shù)據(jù)。
這是一個(gè)"POST"請(qǐng)求的例子:
http.createServer(function (request, response) {
var body = [];
console.log(request.method) ;
console.log(request.headers) ;
request.on('data', function (chunk) {
body.push(chunk);
}) ;
request.on('end', function () {
body = Buffer.concat(body) ;
console.log(body.toString()) ;
});
}).listen(8888) ;
下是一個(gè)完整的“Http”請(qǐng)求數(shù)據(jù)內(nèi)容。
POST / HTTP/1.1
User-Agent: curl/7.26.0
Host: localhost
Accept: */*
Content-Length: 11
Content-Type: application/x-www-form-urlencoded
Hello World
四,總結(jié)一下
(1),理解 "Http" 概念。
(2),熟練使用 "Http" 相關(guān)的api。
(3),注意細(xì)節(jié)的把控,比如:“POST,GET” 之間的處理細(xì)節(jié)。
(4),"requestListener"的理解。
(5),強(qiáng)調(diào)一個(gè)概念:一個(gè)http request對(duì)象是可讀流,而http response對(duì)象則是可寫流 。