一,開篇分析
首先“Http”這個概念大家應該比較熟悉了,它不是基于特定語言的,是一個通用的應用層協定,不同語言有不同的實作細節,但是萬變不離其宗,思想是相同的,
NodeJS作為一個宿主運作環境,以JavaScript為宿主語言,它也有自己實作的一套标準,這篇文章我們就一起來學習一下 “Http子產品” 。但是作為前提來說,
希望大家可以先閱讀一下官網提供的api,有一個前置了解,這樣就友善多了,以下是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
讓我們先從一個簡單例子開始,建立一個叫server.js的檔案,并寫入以下代碼:
1 var http = require('http') ;
2 var server = http.createServer(function(req,res){
3 res.writeHeader(200,{
4 'Content-Type' : 'text/plain;charset=utf-8' // 添加charset=utf-8
5 }) ;
6 res.end("Hello,大熊!") ;
7 }) ;
8 server.listen(8888) ;
9 console.log("http server running on port 8888 ...") ;
(node server.js)以下是運作結果:
二,細節分析執行個體
具體看一下這個小例子:
(1行):通過"require"引入NodeJS自帶的"http"子產品,并且把它指派給http變量。
(2行):調用http子產品提供的函數:"createServer" 。這個函數會傳回一個新的web伺服器對象。
參數 "
requestListener"
是一個函數,它将會自動加入到 "
request"
事件的監聽隊列。
當一個request到來時,Event-Loop會将這個Listener回調函數放入執行隊列, node中所有的代碼都是一個一個從執行隊列中拿出來執行的。
這些執行都是在工作線程上(Event Loop本身可以認為在一個獨立的線程中,我們一般不提這個線程,而将node稱呼為一個單線程的執行環境),
所有的回調都是在一個工作線程上運作。
我們在再來看一下"
requestListener"
這個回調函數,它提供了兩個參數(request,response),
每次收到一個請求時觸發。注意每個連接配接又可能有多個請求(在
keep-alive
的連接配接中)。
"
request"
是
http.IncomingMessage
的一個執行個體。"
response"
是
http.ServerResponse
的一個執行個體。
一個http request對象是可讀流,而http response對象則是可寫流。
一個"
IncomingMessage"
對象是由
http.Server
或
http.ClientRequest
建立的,
并作為第一參數分别傳遞給
"request"
和
"response"
事件。
它也可以被用來通路應答的狀态,頭檔案和資料。
它實作了 "Stream" 接口以及以下額外的事件,方法和屬性。(具體參考api)。
(3行):“writeHeader”,使用 "response.writeHead()" 函數發送一個Http狀态200和Http頭的内容類型(content-type)。
向請求回複響應頭。"statusCode"是一個三位是的HTTP狀态碼,例如
404
。最後一個參數,"
headers",
是響應頭的内容。
舉個栗子:
1 var body = 'hello world' ;
2 response.writeHead(200, {
3 'Content-Length': body.length,
4 'Content-Type': 'text/plain'
5 }) ;
注意:Content-Length 是以位元組(byte)計算,而不是以字元(character)計算。
之前的例子原因是字元串 “Hello World !” 隻包含了單位元組的字元。
如果body包含了多位元組編碼的字元,就應當使用Buffer.byteLength()來确定在多位元組字元編碼情況下字元串的位元組數。
需要進一步說明的是Node不檢查Content-Lenth屬性和已傳輸的body長度是否吻合。
statusCode是一個三位是的HTTP狀态碼, 例如:"
404" 。這裡要說的是 "
http.STATUS_CODES
" ,全部标準"Http"響應狀态碼的集合和簡短描述都在裡面。
如下是源碼參考:
1 var STATUS_CODES = exports.STATUS_CODES = {
2 100 : 'Continue',
3 101 : 'Switching Protocols',
4 102 : 'Processing', // RFC 2518, obsoleted by RFC 4918
5 200 : 'OK',
6 201 : 'Created',
7 202 : 'Accepted',
8 203 : 'Non-Authoritative Information',
9 204 : 'No Content',
10 205 : 'Reset Content',
11 206 : 'Partial Content',
12 207 : 'Multi-Status', // RFC 4918
13 300 : 'Multiple Choices',
14 301 : 'Moved Permanently',
15 302 : 'Moved Temporarily',
16 303 : 'See Other',
17 304 : 'Not Modified',
18 305 : 'Use Proxy',
19 307 : 'Temporary Redirect',
20 400 : 'Bad Request',
21 401 : 'Unauthorized',
22 402 : 'Payment Required',
23 403 : 'Forbidden',
24 404 : 'Not Found',
25 405 : 'Method Not Allowed',
26 406 : 'Not Acceptable',
27 407 : 'Proxy Authentication Required',
28 408 : 'Request Time-out',
29 409 : 'Conflict',
30 410 : 'Gone',
31 411 : 'Length Required',
32 412 : 'Precondition Failed',
33 413 : 'Request Entity Too Large',
34 414 : 'Request-URI Too Large',
35 415 : 'Unsupported Media Type',
36 416 : 'Requested Range Not Satisfiable',
37 417 : 'Expectation Failed',
38 418 : 'I\'m a teapot', // RFC 2324
39 422 : 'Unprocessable Entity', // RFC 4918
40 423 : 'Locked', // RFC 4918
41 424 : 'Failed Dependency', // RFC 4918
42 425 : 'Unordered Collection', // RFC 4918
43 426 : 'Upgrade Required', // RFC 2817
44 500 : 'Internal Server Error',
45 501 : 'Not Implemented',
46 502 : 'Bad Gateway',
47 503 : 'Service Unavailable',
48 504 : 'Gateway Time-out',
49 505 : 'HTTP Version not supported',
50 506 : 'Variant Also Negotiates', // RFC 2295
51 507 : 'Insufficient Storage', // RFC 4918
52 509 : 'Bandwidth Limit Exceeded',
53 510 : 'Not Extended' // RFC 2774
54 };
節選自,Nodejs源碼 ”http.js“ 143行開始。
其實從用戶端應答結果也不難看出:
(6行):”response.end“------當所有的響應報頭和封包被發送完成時這個方法将信号發送給伺服器。伺服器會認為這個消息完成了。
每次響應完成之後必須調用該方法。如果指定了參數 “data” ,就相當于先調用 “response.write(data, encoding) ” 之後再調用 “response.end()” 。
(8行):”server.listen(8888)“ ------ 伺服器用指定的句柄接受連接配接,綁定在特定的端口。
以上就是一個比較詳細的分析過程,希望有助于加深了解,代碼雖然不多,但是重在了解一些細節機制,以便日後高效的開發NodeJS應用。
三,執行個體賞心
除了可以使用"
request"
對象通路請求頭資料外,還能把"
request"
對象當作一個隻讀資料流來通路請求體資料。
這是一個"POST"請求的例子:
1 http.createServer(function (request, response) {
2 var body = [];
3 console.log(request.method) ;
4 console.log(request.headers) ;
5 request.on('data', function (chunk) {
6 body.push(chunk);
7 }) ;
8 request.on('end', function () {
9 body = Buffer.concat(body) ;
10 console.log(body.toString()) ;
11 });
12 }).listen(8888) ;
下是一個完整的“Http”請求資料内容。
1 POST / HTTP/1.1
2 User-Agent: curl/7.26.0
3 Host: localhost
4 Accept: */*
5 Content-Length: 11
6 Content-Type: application/x-www-form-urlencoded
7 Hello World
四,總結一下
(1),了解 "Http" 概念。
(2),熟練使用 "Http" 相關的api。
(3),注意細節的把控,比如:“POST,GET” 之間的處理細節。
(4),"requestListener"的了解。
(5),強調一個概念:一個http request對象是可讀流,而http response對象則是可寫流 。
哈哈哈,本篇結束,未完待續,希望和大家多多交流夠溝通,共同進步。。。。。。呼呼呼……(*^__^*)