NodeJS學習筆記 (6)網絡服務-http-res(ok)


原文:https://github.com/chyingp/nodejs-learning-guide

 

自己敲代碼:

 

概覽

http模塊四劍客之一的res,應該都不陌生了。一個web服務程序,接受到來自客戶端的http請求后,向客戶端返回正確的響應內容,這就是res的職責。

返回的內容包括:狀態代碼/狀態描述信息、響應頭部、響應主體。下文會舉幾個簡單的例子。

var http = require('http'); var server = http.createServer(function(req, res){ res.send('ok'); }); server.listen(3000);


例子

在下面的例子中,我們同時設置了 狀態代碼/狀態描述信息、響應頭部、響應主體,就是這么簡單。

var http = require('http'); // 設置狀態碼、狀態描述信息、響應主體 var server = http.createServer(function(req, res){ res.writeHead(200, 'ok', { 'Content-Type': 'text/plain' }); res.end('hello'); }); server.listen(3000);


設置狀態代碼、狀態描述信息

res提供了 res.writeHead()、res.statusCode/res.statusMessage 來實現這個目的。

舉例,如果想要設置 200/ok ,可以

res.writeHead(200, 'ok');

也可以

res.statusCode = 200; res.statusMessage = 'ok';

兩者差不多,差異點在於

  1. res.writeHead() 可以提供額外的功能,比如設置響應頭部。
  2. 當響應頭部發送出去后,res.statusCode/res.statusMessage 會被設置成已發送出去的 狀態代碼/狀態描述信息。

設置響應頭部

res提供了 res.writeHead()、response.setHeader() 來實現響應頭部的設置。

舉例,比如想把 Content-Type 設置為 text-plain,那么可以

// 方法一 res.writeHead(200, 'ok', { 'Content-Type': 'text-plain' }); // 方法二 res.setHeader('Content-Type', 'text-plain');

兩者的差異點在哪里呢?

  1. res.writeHead() 不單單是設置header。
  2. 已經通過 res.setHeader() 設置了header,當通過 res.writeHead() 設置同名header,res.writeHead() 的設置會覆蓋之前的設置。

關於第2點差異,這里舉個例子。下面代碼,最終的 Content-Type 為 text/plain

var http = require('http'); var server = http.createServer(function(req, res){ res.setHeader('Content-Type', 'text/html'); res.writeHead(200, 'ok', { 'Content-Type': 'text/plain' }); res.end('hello'); }); server.listen(3000);

而下面的例子,則直接報錯。報錯信息為 Error: Can't set headers after they are sent.

var http = require('http'); var server = http.createServer(function(req, res){ res.writeHead(200, 'ok', { 'Content-Type': 'text/plain' }); res.setHeader('Content-Type', 'text/html'); res.end('hello'); }); server.listen(3000);


其他響應頭部操作

增、刪、改、查 是配套的。下面分別舉例說明下,例子太簡單就直接上代碼了。

// 增 res.setHeader('Content-Type', 'text/plain'); // 刪 res.removeHeader('Content-Type'); // 改 res.setHeader('Content-Type', 'text/plain'); res.setHeader('Content-Type', 'text/html'); // 覆蓋 // 查 res.getHeader('content-type');

其中略顯不同的是 res.getHeader(name),name 用的是小寫,返回值沒做特殊處理。

res.setHeader('Content-Type', 'TEXT/HTML'); console.log( res.getHeader('content-type') ); // TEXT/HTML res.setHeader('Content-Type', 'text/plain'); console.log( res.getHeader('content-type') ); // text/plain

此外,還有不那么常用的:

  • res.headersSent:header是否已經發送;
  • res.sendDate:默認為true。但為true時,會在response header里自動設置Date首部。

 

設置響應主體

主要用到 res.write() 以及 res.end() 兩個方法。

res.write() API的信息量略大,建議看下官方文檔

response.write(chunk[, encoding][, callback])

  • chunk:響應主體的內容,可以是string,也可以是buffer。當為string時,encoding參數用來指明編碼方式。(默認是utf8)
  • encoding:編碼方式,默認是 utf8。
  • callback:當響應體flushed時觸發。(TODO 這里想下更好的解釋。。。)

使用上沒什么難度,只是有些注意事項:

  1. 如果 res.write() 被調用時, res.writeHead() 還沒被調用過,那么,就會把header flush出去。
  2. res.write() 可以被調用多次。
  3. 當 res.write(chunk) 第一次被調用時,node 會將 header 信息 以及 chunk 發送到客戶端。第二次調用 res.write(chunk) ,node 會認為你是要streaming data(WTF,該怎么翻譯)。。。

Returns true if the entire data was flushed successfully to the kernel buffer. Returns false if all or part of the data was queued in user memory. 'drain' will be emitted when the buffer is free again.

response.end([data][, encoding][, callback])

掌握了 res.write() 的話,res.end() 就很簡單了。res.end() 的用處是告訴nodejs,header、body都給你了,這次響應就到這里吧。

有點像個語法糖,可以看成下面兩個調用的組合。至於callback,當響應傳遞結束后觸發。

res.write(data, encoding); res.end()


chunk數據

參考這里:http://stackoverflow.com/questions/6258210/how-can-i-output-data-before-i-end-the-response

也就是說,除了nodejs的特性,還需要了解 HTTP協議、瀏覽器的具體實現。(細思極恐)

如果是 text/html

var http = require('http'); http.createServer(function(req, res) { res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.write('hello'); setTimeout(function() { res.write(' world!'); res.end(); }, 2000); }).listen(3000);

如果是 text/plain

var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', 'X-Content-Type-Options': 'nosniff' }); res.write('hello'); setTimeout(function(){ res.write('world'); res.end() }, 2000); }).listen(3000);

失敗例子

var http = require('http'); var server = http.createServer(function(req, res){ res.writeHead(200, 'ok', { 'Content-Type': 'text/html' }); res.write('hello'); setTimeout(function(){ res.write('world'); res.end(); }, 2000); }); server.listen(3000);


超時處理

接口:response.setTimeout(msecs, callback)

關於 timeout 事件的說明,同樣是言簡意賅(WTF),話少信息量大,最好來個demo TODO

If no 'timeout' listener is added to the request, the response, or the server, then sockets are destroyed when they time out. If you assign a handler on the request, the response, or the server's 'timeout' events, then it is your responsibility to handle timed out sockets.

事件 close/finish

  • close:response.end() 被調用前,連接就斷開了。此時會觸發這個事件。
  • finish:響應header、body都已經發送出去(交給操作系統,排隊等候傳輸),但客戶端是否實際收到數據為止。(這個事件后,res 上就不會再有其他事件觸發)

其他不常用屬性/方法

  • response.finished:一開始是false,響應結束后,設置為true。
  • response.sendDate:默認是true。是否自動設置Date頭部。(按HTTP協議是必須要的,除非是調試用,不然不要設置為false)
  • response.headersSent:只讀屬性。響應頭部是否已發送。
  • response.writeContinue():發送 HTTP/1.1 100 Continue 消息給客戶端,提示說服務端願意接受客戶端的請求,請繼續發送請求正文(body)。(TODO 做個demo啥的是大大的好)

相關鏈接

How can I output data before I end the response? http://stackoverflow.com/questions/6258210/how-can-i-output-data-before-i-end-the-response

8.2.3 Use of the 100 (Continue) Status http://greenbytes.de/tech/webdav/rfc2616.html#use.of.the.100.status

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM