Node入門教程(12)第十章:Node的HTTP模塊

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,提供了以下的事件:

  1. request:當(dāng)客戶端請求到來的時(shí)候卿啡,該事件被觸發(fā)吟吝,提供兩個(gè)參數(shù)request和response,分別是http.ServerRequest和http.ServerResponse表示請求和響應(yīng)的信息颈娜。
  2. connection:當(dāng)TCP建立連接的時(shí)候剑逃,該事件被觸發(fā),提供了一個(gè)參數(shù)socket官辽,為net.socket的實(shí)例(底層協(xié)議對象)
  3. close:當(dāng)服務(wù)器關(guān)閉的時(shí)候會被觸發(fā)
  4. 除此之外還有checkContinue蛹磺、upgradeclientError等事件

我們最常用的還是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.Serverrequest事件發(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


老馬免費(fèi)視頻教程

返回教程列表首頁

github地址:https://github.com/malun666/aicoder_node

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市证逻,隨后出現(xiàn)的幾起案子乐埠,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丈咐,死亡現(xiàn)場離奇詭異瑞眼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棵逊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門伤疙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辆影,你說我怎么就攤上這事徒像。” “怎么了蛙讥?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵锯蛀,是天一觀的道長。 經(jīng)常有香客問我次慢,道長谬墙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任经备,我火速辦了婚禮,結(jié)果婚禮上部默,老公的妹妹穿的比我還像新娘侵蒙。我一直安慰自己,他們只是感情好傅蹂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布纷闺。 她就那樣靜靜地躺著,像睡著了一般份蝴。 火紅的嫁衣襯著肌膚如雪犁功。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天婚夫,我揣著相機(jī)與錄音浸卦,去河邊找鬼。 笑死案糙,一個(gè)胖子當(dāng)著我的面吹牛限嫌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播时捌,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼怒医,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奢讨?” 一聲冷哼從身側(cè)響起稚叹,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后扒袖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塞茅,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年僚稿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凡桥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚀同,死狀恐怖缅刽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蠢络,我是刑警寧澤衰猛,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站刹孔,受9級特大地震影響啡省,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜髓霞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一卦睹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧方库,春花似錦结序、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邀层,卻和暖如春返敬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寥院。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工劲赠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秸谢。 一個(gè)月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓经磅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钮追。 傳聞我的和親對象是個(gè)殘疾皇子预厌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345