首頁 > 軟體

極簡 Node.js 入門

2020-10-14 09:30:25

極簡 Node.js 入門系列教學:https://www.yuque.com/sunluyong/node

本文更佳閱讀體驗:https://www.yuque.com/sunluyong/node/http-server

使用 Node.js 建立 http 伺服器需要使用內建的 http 模組

建立 web server

Node.js 是執行在伺服器環境的 JavaScript,這裡的伺服器更多指的是物理概念的伺服器,也就是主機。使用 Node.js 建立 HTTP 伺服器指的是軟體概念的伺服器,也就是 web server,類似於 nginx、apache

const http = require('http');

const server = http.createServer((req, res) => {
	res.write('Hellon');
  res.end();
});

server.listen(9527, () => {
	console.log('Web Server started at port 9527');
});

上面 10 行程式碼建立了一個最簡單的 HTTP 伺服器,伺服器監聽埠號 9527,接收到請求後返回字串 Hellon ,可以使用瀏覽器或者 curl 工具測試

createServer 的回撥函數在接收到請求後被呼叫

req

req 代表本次 http request,是一個可讀流,常用有幾個屬性

  • url:本地請求的地址
  • method:HTTP 請求的方法(GET、POST、DELETE、PUT 等)
  • headers::請求的 HTTP header

res

res 代表本次http response,是一個可寫流,常用的屬性方法有

  • writeHead(statusCode,[, StatusMessage[, headers]]):傳送響應首部,包含狀態碼、狀態資訊、響應頭
  • write(chunk):向響應主體中寫入字串或者 buffer
  • end(chunk):向伺服器發出訊號,可以攜帶最後傳送的資料,表明已傳送所有響應頭和主體,每個響應都需要呼叫一次
  • getHeader(name):返回指定 name 的 header
  • getHeaders():返回包含了所有 header 資訊的物件
  • setHeader(name, value):設定響應頭,和 writeHead() 合併,有衝突時優先使用 writeHead()
  • statusCode:設定響應 HTTP status

返回請求資訊的 web server

上面例子中所有請求返回的結果都一樣,可以對請求識別,做一些差異化的處理,下面例子展示瞭如何把每次請求的基本資訊返回

const http = require('http');

const server = http.createServer((req, res) => {
  const { url, method, headers } = req;

  res.setHeader('content-type', 'text/html');

  res.write(`請求 URL: ${url}n`);
  res.write(`請求方法: ${method}n`);
  res.write(`請求 headers:${JSON.stringify(headers, null, '  ')}`);

  res.end('n');
});

server.listen(9527, () => {
  console.log('Web Server started at port 9527');
});

使用 curl 或者瀏覽器測試

返回檔案內容

上面例子和真實的 web server 還有很大差距,下面例子展示了一個最簡單的返回檔案內容的靜態資源伺服器

const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime-types');

// 靜態資源根目錄,可以設定為原生的任意有許可權目錄,放入 a.jpg 測試
const ROOT_DIRECTORY = '/public';

const server = http.createServer((req, res) => {
  const { url } = req;

  const filePath = path.join(ROOT_DIRECTORY, url);

  fs.readFile(filePath, (err, chunk) => {
    if (err) {
      res.writeHead(404, {
        'content-type': 'text/html',
      });
      res.end('檔案不存在!');

    } else {
      res.writeHead(200, {
        'content-type': mime.contentType(path.extname(url)),
      });
      res.end(chunk);
    }
  });
});

server.listen(9527, () => {
  console.log('Web Server started at port 9527');
});

demo 中使用了 mime-types 包來根據檔名稱獲取檔案的 Content-Type,執行 demo 需要在程式碼目錄安裝 mime-types 
tnpm i -S mime-types

HTTP 狀態碼

  1. 200 OK,表示請求正常處理
  2. 404 Not Found,表示請求資源在伺服器不存在


在測試目錄下放入圖片 a.jpg 使用瀏覽器測試 127.0.0.1:9527/a.jpg 

讀取電影檔案

理論上讀取電影檔案可以使用和上面一樣的程式碼,但實際執行會發現電影檔案在完全讀取到記憶體後才返回給瀏覽器,這樣返回內容耗時極長,而且電影檔案過大的話程式也沒有辦法處理,HTTP 協定是支援分段傳輸的(Transfer-Encoding: chunked),既然 res 是可寫流,可以簡單使用 stream 來做到邊讀取內容邊返回給瀏覽器,而不是一次讀取完成後返回

const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime-types');

// 靜態資源根目錄
const ROOT_DIRECTORY = '/Users/undefined/node-demo/public';

const server = http.createServer((req, res) => {
  const { url } = req;

  const filePath = path.join(ROOT_DIRECTORY, url);

  fs.access(filePath, fs.constants.R_OK, err => {
    if (err) {
      res.writeHead(404, {
        'content-type': 'text/html',
      });
      res.end('檔案不存在!');

    } else {
      res.writeHead(200, {
        'content-type': mime.contentType(path.extname(url)),
      });
      fs.createReadStream(filePath).pipe(res);
    }
  });
});

server.listen(9527, () => {
  console.log('Web Server started at port 9527');
});

使用 stream 章節介紹的 fs.createReadStream() 和 pipe() 可以輕鬆將檔案匯入 http response


Node.js 官網一次 HTTP 傳輸解析對 HTTP Server 做了入門講解,順便介紹了一些 HTTP 協定的相關知識,值得閱讀


IT145.com E-mail:sddin#qq.com