極簡 Node.js 入門 - 5.1 創建 HTTP 服務器


極簡 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('Hello\n');
  res.end();
});

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

上面 10 行代碼創建了一個最簡單的 HTTP 服務器,服務器監聽端口號 9527,接收到請求后返回字符串 Hello\n ,可以使用瀏覽器或者 curl 工具測試
image.png
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 或者瀏覽器測試
image.png

返回文件內容

上面例子和真實的 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 
image.png

讀取電影文件

理論上讀取電影文件可以使用和上面一樣的代碼,但實際執行會發現電影文件在完全讀取到內存后才返回給瀏覽器,這樣返回內容耗時極長,而且電影文件過大的話程序也沒有辦法處理,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 協議的相關知識,值得閱讀


免責聲明!

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



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