Ryan Dahl開發(fā)node的初衷就是:把Nginx
非阻塞IO功能和一個(gè)高度封裝的WEB服務(wù)器結(jié)合在一起的東東廷粒。所以Node初衷就是為了高性能的Web服務(wù)器去的,所以:Node的HTTP模塊也是核心的核心赘那。
本文需要您了解的前置知識點(diǎn):
- HTTP協(xié)議
- Web請求模型:請求→處理→響應(yīng)
- Node的流、事件
http模塊的客戶端
要使用 HTTP 服務(wù)器與客戶端氯质,需要 require('http')
模塊。http模塊提供了兩個(gè)函數(shù)http.request()
和http.get()
,幫助程序向服務(wù)器端發(fā)送請求祠斧。
我們可以通過http.request ()
方法創(chuàng)建一個(gè)發(fā)送請求的http.ClientRequest
類實(shí)例闻察,請求創(chuàng)建后,并不會立即發(fā)送請求琢锋,我們還可以繼續(xù)訪問請求頭:setHeader(name, value)
辕漂、getHeader(name)
和 removeHeader(name)
API 進(jìn)行修改。實(shí)際的請求頭會與第一個(gè)數(shù)據(jù)塊一起發(fā)送或當(dāng)調(diào)用 request.end()
時(shí)發(fā)送吴超。
http.ClientRequest類
-
http.ClientRequest
類繼承了EventEmitter
,它內(nèi)部定義了以下事件钉嘹。
事件 | 說明 |
---|---|
'abort' | 當(dāng)請求已被客戶端終止時(shí)觸發(fā)。 該事件僅在首次調(diào)用 abort() 時(shí)觸發(fā)鲸阻。 |
'connect' | 每當(dāng)服務(wù)器響應(yīng) CONNECT 請求時(shí)觸發(fā)跋涣。 如果該事件未被監(jiān)聽,則接收到 CONNECT 方法的客戶端會關(guān)閉連接鸟悴。 |
'continue' | 當(dāng)服務(wù)器發(fā)送了一個(gè) 100 Continue 的 HTTP 響應(yīng)時(shí)觸發(fā)陈辱,通常是因?yàn)檎埱蟀?Expect: 100-continue。 這是客戶端將要發(fā)送請求主體的指令细诸。 |
'response' | 當(dāng)請求的響應(yīng)被接收到時(shí)觸發(fā)沛贪。 該事件只觸發(fā)一次。如果沒有添加 'response' 事件處理函數(shù)震贵,則響應(yīng)會被整個(gè)丟棄利赋。 如果添加了 'response' 事件處理函數(shù),則必須消耗完響應(yīng)對象的數(shù)據(jù)猩系,可通過調(diào)用 response.read()媚送、或添加一個(gè) 'data' 事件處理函數(shù)、或調(diào)用 .resume() 方法蝙眶。 數(shù)據(jù)被消耗完時(shí)會觸發(fā) 'end' 事件季希。 在數(shù)據(jù)被讀取完之前會消耗內(nèi)存,可能會造成 'process out of memory' 錯(cuò)誤幽纷。 |
'socket' | 當(dāng) socket 被分配到請求后觸發(fā)式塌。 |
'timeout' | 當(dāng)?shù)讓?socket 超時(shí)的時(shí)候觸發(fā)。該方法只會通知空閑的 socket友浸。請求必須手動(dòng)停止峰尝。 |
'upgrade' | 每當(dāng)服務(wù)器響應(yīng) upgrade 請求時(shí)觸發(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é)束時(shí)會被調(diào)用褪储。 |
request.flushHeaders() |
無 | 刷新請求頭。出于效率的考慮慧域,Node.js 通常會緩存請求頭直到 request.end() 被調(diào)用或第一塊請求數(shù)據(jù)被寫入鲤竹。 然后 Node.js 會將請求頭和數(shù)據(jù)打包成一個(gè)單一的 TCP 數(shù)據(jù)包。 |
request.getHeader(name) |
①name ②返回字符串 | 讀出請求頭昔榴,注意:參數(shù)name是大小寫敏感的 |
request.removeHeader(name) |
name 字符串 | 移除一個(gè)已經(jīng)在 headers 對象里面的 header辛藻。 |
request.setHeader(name, value) |
①name是header的key②value | 為 headers 對象設(shè)置一個(gè)單一的 header 值。如果該 header 已經(jīng)存在了论泛,則將會被替換揩尸。這里使用一個(gè)字符串?dāng)?shù)組來設(shè)置有相同名稱的多個(gè) headers。 |
request.setSocketKeepAlive([enable][, initialDelay]) |
①enable類型boolean②initialDelay | 一旦 socket 被分配給請求且已連接屁奏,socket.setKeepAlive() 會被調(diào)用岩榆。 |
request.setTimeout(timeout[, callback]) |
①timeout請求被認(rèn)為是超時(shí)的毫秒數(shù)。②callback 可選的函數(shù),當(dāng)超時(shí)發(fā)生時(shí)被調(diào)用坟瓢。 | 等同于綁定到 timeout 事件勇边。一旦socket被分配給請求且已連接,socket.setTimeout() 會被調(diào)用折联。 |
request.write(chunk[, encoding][, callback]) |
①chunk發(fā)送的請求數(shù)據(jù)粒褒。②encoding:編碼;③callback回調(diào)函數(shù) | 發(fā)送請求主體的一個(gè)數(shù)據(jù)塊诚镰。 通過多次調(diào)用該方法奕坟,一個(gè)請求主體可被發(fā)送到一個(gè)服務(wù)器,在這種情況下清笨,當(dāng)創(chuàng)建請求時(shí)月杉,建議使用 ['Transfer-Encoding', 'chunked'] 請求頭。 |
發(fā)送GET請求
// 引入http模塊
const http = require('http');
// 創(chuàng)建一個(gè)請求
let request = http.request(
{
protocol: 'http:', // 請求的協(xié)議
host: 'aicoder.com', // 請求的host
port: 80, // 端口
method: 'GET', // GET請求
timeout: 2000, // 超時(shí)時(shí)間
path: '/' // 請求路徑
},
res => { // 連接成功后抠艾,接收到后臺服務(wù)器返回的響應(yīng)苛萎,回調(diào)函數(shù)就會被調(diào)用一次。
// res => http.IncomingMessage : 是一個(gè)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請求的另外一個(gè)辦法
http模塊還提供了http.get(options,callback)
腌歉,用來更簡單的處理GET方式的請求蛙酪,它是http.request()
的簡化版本,唯一的區(qū)別在于http.get自動(dòng)將請求方法設(shè)為GET請求翘盖,同時(shí)不需要手動(dòng)調(diào)用req.end();
http.get('http://aicoder.com', res => {
res.on('data', data => {
console.log(data.toString('utf8'));
});
});
發(fā)送post請求
且看一個(gè)發(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
實(shí)現(xiàn)了簡單的web服務(wù)器馍驯,并把請求和響應(yīng)也做了封裝藐俺。
http.server對象的事件
http.server
是一個(gè)基于事件的HTTP服務(wù)器,所有的請求都被封裝到獨(dú)立的事件當(dāng)中泥彤,我們只需要對他的事件編寫相應(yīng)的行數(shù)就可以實(shí)現(xiàn)HTTP服務(wù)器的所有功能,它繼承自EventEmitter,提供了以下的事件:
-
request
:當(dāng)客戶端請求到來的時(shí)候卿啡,該事件被觸發(fā)吟吝,提供兩個(gè)參數(shù)request和response,分別是http.ServerRequest和http.ServerResponse表示請求和響應(yīng)的信息颈娜。 -
connection
:當(dāng)TCP建立連接的時(shí)候剑逃,該事件被觸發(fā),提供了一個(gè)參數(shù)socket官辽,為net.socket的實(shí)例(底層協(xié)議對象) -
close
:當(dāng)服務(wù)器關(guān)閉的時(shí)候會被觸發(fā) - 除此之外還有
checkContinue
蛹磺、upgrade
、clientError
等事件
我們最常用的還是request
事件同仆,http也給這個(gè)事件提供了一個(gè)捷徑:http.createServer([requestListener])
下面我們來簡單的看一下兩個(gè)案例:
第一個(gè):使用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);
第二個(gè):利用http.createServer
創(chuàng)建服務(wù)器實(shí)例代碼:
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é)議完成之后立即讀取,請求體可能相對較長一點(diǎn)俗批,需要一定的時(shí)間傳輸俗或。因此提供了三個(gè)事件用于控制請求體傳輸.
1.data
:當(dāng)請求體數(shù)據(jù)到來時(shí),該事件被觸發(fā)岁忘,該事件一共一個(gè)參數(shù)chunk辛慰,表示接受到的數(shù)據(jù)。
1.end
:當(dāng)請求體數(shù)據(jù)傳輸完成時(shí)干像,該事件被觸發(fā)帅腌,此后將不會再有數(shù)據(jù)到來。
1.close
:用戶當(dāng)前請求結(jié)束時(shí)麻汰,該事件被觸發(fā)速客,不同于end,如果用戶強(qiáng)制終止了傳輸什乙,也會觸發(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的實(shí)例 |
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完整的請求路徑辅愿,包括了?后面的部分智亮,因此你可以手動(dòng)解析后面的內(nèi)容作為GET的參數(shù),Nodejs的url模塊中的parse函數(shù)提供了這個(gè)功能点待。
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·并沒有一個(gè)屬性內(nèi)容為請求體,原因是等待請求體傳輸可能是一件耗時(shí)的工作癞埠。譬如上傳文件状原。惡意的POST請求會大大消耗服務(wù)器的資源。所以Nodejs是不會解析請求體苗踪,當(dāng)你需要的時(shí)候颠区,需要手動(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
這個(gè)類實(shí)現(xiàn)了(而不是繼承自)可寫流 接口通铲。繼承了EventEmitter
毕莱。它用來給用戶發(fā)送響應(yīng)結(jié)果,它是由http.Server
的request
事件發(fā)送的颅夺,作為第二個(gè)參數(shù)傳遞朋截。一般為response或res
主要的三個(gè)函數(shù):
-
response.writeHead(statusCode,[headers])
:向請求的客戶端發(fā)送響應(yīng)頭。-
statusCode
是HTTP的狀態(tài)碼吧黄,如200為成功部服,404未找到等。 -
headers
是一個(gè)類似關(guān)聯(lián)數(shù)組的對象拗慨,表示響應(yīng)頭的每個(gè)屬性廓八。
-
-
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