Node.js日記:Web相關

Web 服務器

Web 服務器一般指網站服務器,是指駐留於因特網上某種類型計算機的程序,主要功能是提供網上信息瀏覽服務。

用戶通過瀏覽器遵循 HTTP 協議訪問 Web 服務器上所提供信息。

目前最主流的三個 Web 服務器是 Apache、Nginx、IIS。

Web 應用開發

Web 應用程序是一種部署在 Web 服務器中的,可以通過 HTTP 協議訪問的應用程序。

可以通過什麼來開發Web應用?

1)通過Java等服務端語言

大多數 Web 服務器都支持服務端語言(PHP、Java)等,通過這些語言開發的應用程序可以部署到 Web 服務器中,來處理用戶的請求及響應數據。

2)通過JavaScript

Node.js 提供了 http 模塊,http 模塊主要用於搭建 HTTP 服務端,即代表可以使用 JavaScript 語言來開發Web應用,處理用戶請求及響應。

Node.js 標準庫提供了 http 模塊,其中封裝了一個高效的 HTTP 服務器和一個簡易的 HTTP 客戶端

http.Server 是一個基於事件的 HTTP 服務器,它的核心由 Node.js 下層 C++ 部分實現,而接口由 JavaScript 封裝,兼顧了高性能與簡易性;

http.request 則是一個 HTTP 客戶端工具,用於向 HTTP 服務器發起請求。

HTTP 協議

超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最爲廣泛的一種網絡協議,用於定義 Web 瀏覽器與 Web 服務器之間交換數據的過程規則,以及數據本身的格式。

協議主要規定以下幾個部分:

1)Request 消息的結構

- 請求行:包括 http 請求的種類(GET 或 POST 等),請求資源的路徑,http 協議版本。

- 請求頭:http 頭部信息。

- 空行。

- 請求體:發送給服務器的 query 信息。

2)Response 消息的結構

- 狀態行:協議版本、狀態碼。

- 響應頭:響應頭信息。

- 空行。

- 響應體:響應請求的資源。

3)狀態碼

HTTP響應狀態代碼指示特定 HTTP 請求是否已成功完成。

響應分爲五類:信息響應,成功響應,重定向,客戶端錯誤和服務器錯誤。

常見的有:

- 200 - 請求成功

- 302 - 重定向

- 404 - 請求的資源(網頁等)不存在

- 500 - 服務器發生了不可預期的錯誤

4)數據響應類型

HTTP 中使用 MIME 類型來代表響應數據的類型:

- text:    text/plain

- html:    text/html

- xml:     application/xml

- json:    application/json  

- jpg:     image/jpeg

- png:     image/png

HTTP 服務器

http.Server 是 http 模塊中的 HTTP 服務器對象,用 Node.js 做的所有基於 HTTP 協議做的 Web 應用,都是基於 http.Server 實現的。

它提供了一套封裝級別很低的 API,僅僅是流控制和簡單的消息解析,所有的高層功能都要通過它的接口來實現。

const http = require('http');

// http.createServer 創建了一個 http.Server 的實例,將一個函數作爲 HTTP 請求處理函數
let server = http.createServer((req, res) => {
    // 發送 HTTP 頭部 
    // HTTP 狀態值: 200 : OK,內容類型: text/plain
    res.writeHead(200, {'Content-Type': 'text/plain'});
    // 發送響應數據 "Hello World"
    res.end('Hello World');
});
server.listen(8888);    // 監聽 8888 端口 

http.Server

http.Server 是一個基於事件的 HTTP 服務器,所有的請求都被封裝爲獨立的事件,開發者只需要對它的事件編寫響應函數即可實現 HTTP 服務器的所有功能。

常用的事件:

- request:當客戶端請求到來時,該事件被觸發,提供兩個參數 req 和 res ,分別是 http.ServerRequest 和  http.ServerResponse 的實例,表示請求和響應信息。

最常用的就是  request 了,因此 http 提供了一個捷徑:http.createServer([requestListener]),功能是創建一個 HTTP 服務器並將 requestListener 作爲  request 事件的監聽函數。

- close:當服務器關閉時,該事件被觸發。注意不是在用戶連接斷開時。

代碼舉例:

const http = require('http');
let server = new http.Server();
server.on('request', (req, res) => {
    // 設置響應的狀態碼和響應的消息類型
    res.writeHead(200, {'Content-Type': 'text/plain'});
    // 設置響應的內容
    res.end('Hello World');
});
server.listen(8888);

http.ServerResponse

http.ServerResponse 是返回給客戶端的信息,決定了用戶最終能看到的結果。它也是由 http.Server 的 request 事件產生,作爲第二個參數傳遞,一般簡稱爲 response 或 res。

http.ServerResponse 有三個重要的成員函數,用於返回響應頭、響應內容以及結束請求

- response.writeHead(statusCode, [headers]):向請求的客戶端發送響應頭。statusCode 是 HTTP 狀態碼。headers
  是一個類似關聯數組的對象,表示響應頭的每個屬性。該函數在一個請求內最多隻能調用一次,若不調用,則會自動生成一個響應頭。
- response.write(data, [encoding]):向請求的客戶端發送響應內容。 data 是一個 Buffer 或字符串,表示要發送的內容。若 data 是字符串,那麼需要指定 encoding 來說明它的編碼方式,默認是 utf-8。在 response.end 調用之前,
  response.write 可以被多次調用。
- response.end([data], [encoding]):結束響應,告知客戶端所有發送已經完成。當所有要返回的內容發送完畢的時候,該函數 必須被調用一次。它接受兩個可選參數,意義和 response.write 相同。如果不調用該函數,客戶端將永遠處於等待狀態。

靜態資源和動態資源

- 靜態資源:一般客戶端發送請求到 Web 服務器,Web 服務器根據請求的資源從磁盤中取到相應的文件,返回給客戶端,客戶端解析並渲染顯示出來。
- 動態資源:一般客戶端請求的動態資源,先將請求交於 Web 服務器,Web 服務器連接數據庫,從數據庫獲取對應的數據處理之後結果,返回給客戶端解析渲染處理。

1)響應靜態資源

const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('./mime.json');

http.createServer((req, res) => {
    fs.readFile(path.join(__dirname, 'static', req.url), (err, fileContent) => {
        if(err){
            // 沒有找到對應文件
            res.writeHead(404, {'Content-Type' : 'text/plain;charset=utf8'});
            res.end('頁面被外星人劫持了');
        }else{
            let dtype = 'text/html';
            // 獲取請求文件的後綴
            let ext = path.extname(req.url);
            // 如果請求的文件後綴合理,就獲取到標準的響應格式
            if(mime[ext]){
                dtype = mime[ext];
            }
            // 如果響應的內容是文本,就設置成 utf8 編碼
            if(dtype.startsWith('text')){
                dtype += ';charset=utf8'
            }
            // 設置響應頭信息
            res.writeHead(200, {'Content-Type' : dtype});
            res.end(fileContent);
        }
    });
}).listen(8888);

2)響應動態資源

響應 JSON 格式的數據:

const http = require('http');
// 響應 JSON 格式的數據
http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'application/json'});
    // 模擬從數據庫查詢了用戶數據
    res.end('{"name":"lony","age":18}');
}).listen(8888);

響應 HTML 格式的數據:

const http = require('http');
// 響應 HTML 格式的數據
http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type' : 'text/html'});
    res.write('<!DOCTYPE html>');
	res.write('<html lang="en">');
	res.write('<head>');
    res.write('<meta charset="UTF-8">');
    res.write('<title>書籍</title>');
	res.write('</head>');
    res.write('<body>');
    // 模擬從數據庫查詢了書籍數據
    res.write('<ul><li>三國演義</li><li>水滸傳</li><li>西遊記</li><li>紅樓夢</li></ul>');
    res.write('</body>');
	res.write('</html>');
    res.end();
}).listen(8888);

缺點:手動拼接字符串太麻煩了,容易拼接錯。

http.ServerRequest

http.ServerRequest 是 HTTP 請求的信息,是後端開發者最關注的內容。它一般由 http.Server 的 request 事件產生,作爲第一個參數傳遞,通常簡稱 request 或 req 。

ServerRequest 提供一些屬性,如下:

  • complete: 客戶端請求是否已經發送完成

  • httpVersion: HTTP 協議版本,通常是 1.0 或 1.1

  • method: HTTP 請求方法,如 GET、POST、PUT、DELETE 等

  • url: 原始的請求路徑,例如 /static/image/a.jpg

  • headers: HTTP 請求頭

  • trailers: HTTP 請求尾(不常見)

  • connection: 當前 HTTP 連接套接字,爲 net.Socket 的實例

  • socket: connection 屬性的別名

  • client: client 屬性的別名

HTTP 請求一般可以分爲兩部分:請求頭(Request Header)和請求體(Requset Body)

以上內容由於長度較短都可以在請求頭解析完成後立即讀取。

而請求體可能相對較長,需要一定的時間傳輸,因此http.ServerRequest 提供了以下 3 個事件用於控制請求體傳輸:

data:當請求體數據到來時,該事件被觸發。該事件提供一個參數 chunk,表示接收到的數據。若該事件沒有被監聽,那麼請求體將會被拋棄。該事件可能會被調用多次。

end:當請求體數據傳輸完成時,該事件被觸發,此後將不會再有數據到來。

close:用戶當前請求結束時,該事件被觸發。不同於 end,如果用戶強制終止了傳輸,也還是調用 close。

GET方式獲取請求參數

由於 GET 請求,請求參數直接被嵌入在路徑中。而 url 屬性是完整的請求路徑,其包括了 ? 後面的部分,因此你可以手動解析 url 獲取得請求的參數。Node.js 的 url 模塊中的 parse 函數提供了這個功能。

代碼舉例:

const http = require('http');
const url = require('url');
const util = require('util');
http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    let params = util.inspect(url.parse(req.url, true)); // 利用 url 模塊功能解析出請求參數
    res.end(params);
}).listen(8888);

POST方式獲取請求參數

POST 請求的內容全部都在請求體中。http.ServerRequest 並沒有一個屬性內容爲請求體,原因是等待請求體傳輸可能是一件 耗時的工作,譬如上傳文件。

而很多時候可能並不需要理會請求體的內容,惡意的 POST 請求會大大消耗服務器的資源。所以 Node.js 默認是不會解析請求體的,當你需要的時候,需要手動來做。

代碼舉例:

const http = require('http');
const querystring = require('querystring');
const util = require('util');
http.createServer((req, res) => {
    // 定義了一個 postData 變量,用於暫存請求體的信息。
    let postData = '';
    // 每當接受到請求體的數據,就累加到 post 變量中
    req.on('data', function(chunk) {
        postData += chunk;
    });
    // 當請求體數據傳輸完成時,通過 querystring.parse 將 postData 解析爲真正的 POST 請求格式
    req.on('end', function() {
        postData = querystring.parse(postData);
        res.end(util.inspect(postData));
    });
}).listen(8888);

路徑分發

根據用戶請求的路徑不一樣,做不同處理。

代碼舉例:

const http = require('http');
let server = http.createServer((req, res) => {
    // req.url可以獲取URL中的路徑(端口之後部分)
    if(req.url.startsWith('/index')){
        res.end('index');
    }else if(req.url.startsWith('/about')){
        res.end('about');
    }else if(req.url.startsWith('/close')){
        res.end('Server is closed!');
        server.close();
    }else{
        res.end('no content');
    }
});
server.listen(8888);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章