Ryan Dahl開發(fā)node的初衷就是:把Nginx
非阻塞IO功能和一個高度封裝的WEB服務(wù)器結(jié)合在一起的東東。所以Node初衷就是為了高性能的Web服務(wù)器去的乱凿,所以:Node的HTTP模塊也是核心的核心顽素。
本文需要您了解的前置知識點:
- HTTP協(xié)議
- Web請求模型:請求→處理→響應(yīng)
- Node的流、事件
http模塊的客戶端
要使用 HTTP 服務(wù)器與客戶端徒蟆,需要 require('http')
模塊胁出。http模塊提供了兩個函數(shù)http.request()
和http.get()
,幫助程序向服務(wù)器端發(fā)送請求。
我們可以通過http.request ()
方法創(chuàng)建一個發(fā)送請求的http.ClientRequest
類實例段审,請求創(chuàng)建后全蝶,并不會立即發(fā)送請求,我們還可以繼續(xù)訪問請求頭:setHeader(name, value)
、getHeader(name)
和 removeHeader(name)
API 進(jìn)行修改抑淫。實際的請求頭會與第一個數(shù)據(jù)塊一起發(fā)送或當(dāng)調(diào)用 request.end()
時發(fā)送绷落。
http.ClientRequest類
-
http.ClientRequest
類繼承了EventEmitter
,它內(nèi)部定義了以下事件。
事件 | 說明 |
---|---|
'abort' | 當(dāng)請求已被客戶端終止時觸發(fā)始苇。 該事件僅在首次調(diào)用 abort() 時觸發(fā)砌烁。 |
'connect' | 每當(dāng)服務(wù)器響應(yīng) CONNECT 請求時觸發(fā)。 如果該事件未被監(jiān)聽催式,則接收到 CONNECT 方法的客戶端會關(guān)閉連接函喉。 |
'continue' | 當(dāng)服務(wù)器發(fā)送了一個 100 Continue 的 HTTP 響應(yīng)時觸發(fā),通常是因為請求包含 Expect: 100-continue蓄氧。 這是客戶端將要發(fā)送請求主體的指令函似。 |
'response' | 當(dāng)請求的響應(yīng)被接收到時觸發(fā)槐脏。 該事件只觸發(fā)一次喉童。如果沒有添加 'response' 事件處理函數(shù),則響應(yīng)會被整個丟棄顿天。 如果添加了 'response' 事件處理函數(shù)堂氯,則必須消耗完響應(yīng)對象的數(shù)據(jù),可通過調(diào)用 response.read()牌废、或添加一個 'data' 事件處理函數(shù)咽白、或調(diào)用 .resume() 方法。 數(shù)據(jù)被消耗完時會觸發(fā) 'end' 事件鸟缕。 在數(shù)據(jù)被讀取完之前會消耗內(nèi)存晶框,可能會造成 'process out of memory' 錯誤。 |
'socket' | 當(dāng) socket 被分配到請求后觸發(fā)懂从。 |
'timeout' | 當(dāng)?shù)讓?socket 超時的時候觸發(fā)授段。該方法只會通知空閑的 socket。請求必須手動停止番甩。 |
'upgrade' | 每當(dāng)服務(wù)器響應(yīng) upgrade 請求時觸發(fā)侵贵。 如果該事件未被監(jiān)聽,則接收到 upgrade 請求頭的客戶端會關(guān)閉連接缘薛。 |
-
http.ClientRequest
類還提供了一些方法供我們進(jìn)行請求和返回響應(yīng)的處理窍育。
方法 | 參數(shù) | 說明 |
---|---|---|
request.end([data[, encoding]][, callback]) |
①data 發(fā)送的數(shù)據(jù) ②encoding 編碼 ③callback回調(diào)函數(shù) |
結(jié)束發(fā)送請求。如果部分請求主體還未被發(fā)送宴胧,則會刷新它們到流中漱抓。如果請求是分塊的,則會發(fā)送終止字符 '0\r\n\r\n'恕齐。如果指定了 data乞娄,則相當(dāng)于調(diào)用 request.write(data, encoding) 之后再調(diào)用 request.end(callback)。如果指定了 callback,則當(dāng)請求流結(jié)束時會被調(diào)用补胚。 |
request.flushHeaders() |
無 | 刷新請求頭码耐。出于效率的考慮,Node.js 通常會緩存請求頭直到 request.end() 被調(diào)用或第一塊請求數(shù)據(jù)被寫入溶其。 然后 Node.js 會將請求頭和數(shù)據(jù)打包成一個單一的 TCP 數(shù)據(jù)包骚腥。 |
request.getHeader(name) |
①name ②返回字符串 | 讀出請求頭,注意:參數(shù)name是大小寫敏感的 |
request.removeHeader(name) |
name 字符串 | 移除一個已經(jīng)在 headers 對象里面的 header瓶逃。 |
request.setHeader(name, value) |
①name是header的key②value | 為 headers 對象設(shè)置一個單一的 header 值束铭。如果該 header 已經(jīng)存在了,則將會被替換厢绝。這里使用一個字符串?dāng)?shù)組來設(shè)置有相同名稱的多個 headers契沫。 |
request.setSocketKeepAlive([enable][, initialDelay]) |
①enable類型boolean②initialDelay | 一旦 socket 被分配給請求且已連接,socket.setKeepAlive() 會被調(diào)用昔汉。 |
request.setTimeout(timeout[, callback]) |
①timeout請求被認(rèn)為是超時的毫秒數(shù)懈万。②callback 可選的函數(shù),當(dāng)超時發(fā)生時被調(diào)用。 | 等同于綁定到 timeout 事件靶病。一旦socket被分配給請求且已連接会通,socket.setTimeout() 會被調(diào)用。 |
request.write(chunk[, encoding][, callback]) |
①chunk發(fā)送的請求數(shù)據(jù)娄周。②encoding:編碼涕侈;③callback回調(diào)函數(shù) | 發(fā)送請求主體的一個數(shù)據(jù)塊。 通過多次調(diào)用該方法煤辨,一個請求主體可被發(fā)送到一個服務(wù)器裳涛,在這種情況下,當(dāng)創(chuàng)建請求時众辨,建議使用 ['Transfer-Encoding', 'chunked'] 請求頭端三。 |
發(fā)送GET請求
// 引入http模塊
const http = require('http');
// 創(chuàng)建一個請求
let request = http.request(
{
protocol: 'http:', // 請求的協(xié)議
host: 'aicoder.com', // 請求的host
port: 80, // 端口
method: 'GET', // GET請求
timeout: 2000, // 超時時間
path: '/' // 請求路徑
},
res => { // 連接成功后,接收到后臺服務(wù)器返回的響應(yīng)泻轰,回調(diào)函數(shù)就會被調(diào)用一次技肩。
// res => http.IncomingMessage : 是一個Readable Stream
res.on('data', data => {
console.log(data.toString('utf8')); // 打印返回的數(shù)據(jù)。
});
}
);
// 設(shè)置請求頭部
request.setHeader('Cache-Control', 'max-age=0');
// 真正的發(fā)送請求
request.end();
發(fā)送get請求的另外一個辦法
http模塊還提供了http.get(options,callback)
浮声,用來更簡單的處理GET方式的請求虚婿,它是http.request()
的簡化版本,唯一的區(qū)別在于http.get自動將請求方法設(shè)為GET請求泳挥,同時不需要手動調(diào)用req.end();
http.get('http://aicoder.com', res => {
res.on('data', data => {
console.log(data.toString('utf8'));
});
});
發(fā)送post請求
且看一個發(fā)送post請求的例子然痊。
const http = require('http');
let request = http.request(
{
protocol: 'http:',
host: 'aicoder.com',
port: 80,
method: 'POST',
timeout: 2000,
path: '/'
},
res => {
res.on('data', data => {
console.log(data.toString('utf8'));
});
}
);
// 發(fā)送請求的數(shù)據(jù)。
request.write('id=3&name=aicoder');
request.end();
HTTP服務(wù)器端
http.Server
實現(xiàn)了簡單的web服務(wù)器屉符,并把請求和響應(yīng)也做了封裝剧浸。
http.server對象的事件
http.server
是一個基于事件的HTTP服務(wù)器锹引,所有的請求都被封裝到獨立的事件當(dāng)中,我們只需要對他的事件編寫相應(yīng)的行數(shù)就可以實現(xiàn)HTTP服務(wù)器的所有功能唆香,它繼承自EventEmitter,提供了以下的事件:
-
request
:當(dāng)客戶端請求到來的時候嫌变,該事件被觸發(fā),提供兩個參數(shù)request和response躬它,分別是http.ServerRequest和http.ServerResponse表示請求和響應(yīng)的信息腾啥。 -
connection
:當(dāng)TCP建立連接的時候,該事件被觸發(fā)冯吓,提供了一個參數(shù)socket倘待,為net.socket的實例(底層協(xié)議對象) -
close
:當(dāng)服務(wù)器關(guān)閉的時候會被觸發(fā) - 除此之外還有
checkContinue
、upgrade
组贺、clientError
等事件
我們最常用的還是request
事件凸舵,http也給這個事件提供了一個捷徑:http.createServer([requestListener])
下面我們來簡單的看一下兩個案例:
第一個:使用request事件的:
const http = require('http');
let server = new http.Server();
server.on('request', (req, res) => {
console.log(req.url);
//設(shè)置應(yīng)答頭信息
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('hello we are family<br>');
res.end('server already end\n');
});
//顯示了三次這也證明了TCP的三次握手
server.on('connection', () => {
console.log('握手');
});
server.on('close', () => {
console.log('server will close');
});
//關(guān)閉服務(wù)為了觸發(fā)close事件
server.close();
server.listen(8080);
第二個:利用http.createServer
創(chuàng)建服務(wù)器實例代碼:
const http = require('http');
http.createServer(function(req,res){
res.writeHead(200,{'Content-Type':'text/plain'})
res.write("hi, from aicoder.com");
res.end();
}).listen(3000);
http.ServerRequset請求信息
我們都知道HTTP請求分為兩部分:請求頭和請求體,如果請求的內(nèi)容少的話就直接在請求頭協(xié)議完成之后立即讀取失尖,請求體可能相對較長一點啊奄,需要一定的時間傳輸。因此提供了三個事件用于控制請求體傳輸.
1.data
:當(dāng)請求體數(shù)據(jù)到來時雹仿,該事件被觸發(fā)增热,該事件一共一個參數(shù)chunk,表示接受到的數(shù)據(jù)胧辽。
1.end
:當(dāng)請求體數(shù)據(jù)傳輸完成時,該事件被觸發(fā)公黑,此后將不會再有數(shù)據(jù)到來邑商。
1.close
:用戶當(dāng)前請求結(jié)束時,該事件被觸發(fā)凡蚜,不同于end人断,如果用戶強制終止了傳輸,也會觸發(fā)close
ServerRequest的屬性
名稱 | 含義 |
---|---|
complete | 客戶端請求是否已經(jīng)發(fā)送完成 |
httpVersion | HTTP協(xié)議版本朝蜘,通常是1.0或1.1 |
method | HTTP請求方法恶迈,如:GET,POST |
url | 原始的請求路徑 |
headers | HTTP請求頭 |
trailers | HTTP請求尾(不常見) |
connection | 當(dāng)前HTTP連接套接字,為net.Socket的實例 |
socket | connection屬性的別名 |
client | client屬性的別名 |
http.createServer(function(req,res){
console.log(req.httpVersion);
//console.log(req.socket);
console.log(req.headers);
console.log(req.method);
res.writeHead(404,{'Content-Type':'text/plain'})
res.write("we are is content");
res.end();
}).listen(8080);
獲取GET請求內(nèi)容
由于GET請求直接被嵌入在路徑中,URL完整的請求路徑谱醇,包括了?后面的部分暇仲,因此你可以手動解析后面的內(nèi)容作為GET的參數(shù),Nodejs的url模塊中的parse函數(shù)提供了這個功能副渴。
const http = require('http');
const url = require('url');
const util = require('util');
http
.createServer((req, res) => {
//利用url模塊去解析客戶端發(fā)送過來的URL
res.write(util.inspect(url.parse(req.url, true)));
res.end();
})
.listen(8080);
獲得POST請求內(nèi)容
POST請求的內(nèi)容全部都在請求體中奈附,·http.ServerRequest·并沒有一個屬性內(nèi)容為請求體,原因是等待請求體傳輸可能是一件耗時的工作煮剧。譬如上傳文件斥滤。惡意的POST請求會大大消耗服務(wù)器的資源将鸵。所以Nodejs是不會解析請求體,當(dāng)你需要的時候佑颇,需要手動來做顶掉。
簡單的看一下代碼:
// 獲取post請求數(shù)據(jù)
const http = require('http');
const util = require('util');
const querystring = require('querystring');
http
.createServer((req, res) => {
let post = '';
req.on('data', chunk => {
post += chunk;
});
req.on('end', () => {
post = querystring.parse(post);
res.end(util.inspect(post));
});
})
.listen(60004);
http.ServerResponse返回客戶端信息
http.ServerResponse
這個類實現(xiàn)了(而不是繼承自)可寫流 接口。繼承了EventEmitter
挑胸。它用來給用戶發(fā)送響應(yīng)結(jié)果一喘,它是由http.Server
的request
事件發(fā)送的,作為第二個參數(shù)傳遞嗜暴。一般為response或res
主要的三個函數(shù):
-
response.writeHead(statusCode,[headers])
:向請求的客戶端發(fā)送響應(yīng)頭凸克。-
statusCode
是HTTP的狀態(tài)碼,如200為成功闷沥,404未找到等萎战。 -
headers
是一個類似關(guān)聯(lián)數(shù)組的對象,表示響應(yīng)頭的每個屬性舆逃。
-
-
response.write(data,[encoding])
向請求客戶端發(fā)送相應(yīng)內(nèi)容蚂维,data是buffer或字符串,encoding為編碼 -
response.end([data],[encoding])
結(jié)束響應(yīng)路狮,告知用戶所有發(fā)送已經(jīng)完成虫啥,當(dāng)所有要返回的內(nèi)容發(fā)送完畢,該函數(shù)必須被調(diào)用一次奄妨,如果不調(diào)用涂籽,客戶端永遠(yuǎn)處于等待狀態(tài)
總結(jié)
真正開發(fā)環(huán)境,不會用這么底層的API去做web網(wǎng)站或者微服務(wù)砸抛,一般會選擇KOA或者EXPRESS等框架评雌。不過,通過底層的API也可以感受一下NODE原生的開發(fā)的快樂直焙。
參考:
1.https://blog.csdn.net/woshinannan741/article/details/51357464
1.http://nodejs.cn/api/http.html#http_http_createserver_requestlistener