Node.js搭建WEB服務器


前言

這幾天為了熟悉vue.js框架,還有webpack的使用,就准備搭建一個發布和瀏覽markdwon的簡單WEB應用。原本是想着用bash腳本和busybox的httpd來作為后台服務,但是bash腳本解析和生成JSON非常不方便,而用Java語言寫又覺得部署不方便,所以就想到了正在用到的Node.js,於是就有了這篇博文。(文末有本文代碼的github地址)

簡單例子

首先,從搭建最簡單的Hello world開始,建立以下目錄、文件和內容。

建立項目及運行

project

  web-server
+ | - server.js

server.js

const http = require('http');

http.createServer(function(request, response) {
  // 設置響應頭
  response.writeHeader(200, {
    "Content-Type" : "text/plain"
  });
  // 響應主體為 "Hello world!"
  response.write("Hello world!");
  response.end();
})
// 設置監聽端口為9000
.listen(9000);

現在,在項目目錄運行下面命令來執行server.js,瀏覽器地址欄中輸入localhost:9000,如果一切訪問都正常,瀏覽器就會顯示Hello world!

node server.js

提示:使用ctrl+c停止腳本運行。

至此一個簡單例子就運行成功了,下面來分析一下代碼。

代碼分析

首先,server.js中引入了Node.js的http模塊,它提供了非常底層HTTP API支持。這里使用createServer()方法,它返回一個http.server實例,使用該實例的listen()方法來設置監聽端口。

方法createSever()中填寫的參數是一個函數,該函數會作為回調函數自動添加到request事件去,其參數類型分別為http.IncomingMessagehttp.ServerResponse。在回調函數體里,利用http.ServerResponse的方法設置了響應頭和響應主體,最后以end()方法結束本次請求。

路由功能

上述的例子僅僅實現了簡單請求響應功能,現在增加路由的功能來健壯我們的WEB服務器。現在,修改為以下的目錄、文件和內容。

實現簡單路由

project

  web-server
  | - server.js
+ | - router.js

server.js

const http = require('http');
const router = require('./router.js');

function handleHello(request, response) {
  // 設置響應頭
  response.writeHeader(200, {
    "Content-Type" : "text/plain"
  });
  // 響應主體為 "Hello world!"
  response.write("Hello world!");
  response.end();
}

http.createServer(function(request, response) {
  // 注冊路徑和其對應回調函數
  router.register(request, response, [
    {
      'url': '/hello',
      'handler': handleHello
    }
  ]);
})
// 設置監聽端口為9000
.listen(9000);

router.js

const url = require('url');

exports.register = function(request, response, mapping) {
  // 解析請求路徑
  var pathName = url.parse(request.url).pathname;
  // 執行相應請求路徑的回調函數
  for(let i = 0, len = mapping.length;i < len;i++) {
    if(mapping[i].url === pathName) {
      mapping[i].handler(request, response);
      return;
    }
  }
  // 請求路徑不存在返回404頁面
  response.writeHeader(404, {
    "Content-Type" : "text/html"
  });
  response.end(`
    <html>
      <head>
        <title>NOT FOUND</title>
      </head>
      <body>
        <h1>404 NOT FOUND</h1>
      </body>
    </html>
  `);
}

現在,再次執行server.js腳本,接着瀏覽器訪問localhost:9000\hello會得到Hello world!的結果,而訪問其他路徑則會得到404頁面。

這個功能的核心實現是在router.js中,通過請求路徑的解析,然后根據預先注冊好的mapping數組,找到與之對應的路徑並執行相應的回調函數。

靜態資源請求

當前的路由功能只能實現回調函數的執行,而一個WEB服務器應具有響應靜態資源請求的能力,接下我們繼續來改造它。現在,保持server.js內容不變,只改變router.js中的內容(部分代碼內容省略)。

route.js

const url = require('url');
const path = require('path');
const fs = require('fs');

function getErrorInfo(errorType) {
  // 省略代碼
}

function writeErrorPage(response, errorType) {
  // 省略代碼
}

exports.register = function(request, response, mapping) {
  // 解析請求路徑
  var pathName = url.parse(request.url).pathname;
  // 執行相應請求路徑的回調函數
  for(let i = 0, len = mapping.length;i < len;i++) {
    if(mapping[i].url === pathName) {
      mapping[i].handler(request, response);
      return;
    }
  }
  // 請求路徑為文件返回文件內容
  var file = path.resolve(__dirname, '.' + pathName);
  fs.exists(file, function(exists) {
    // 請求路徑不存在返回404頁面
    if(!exists) {
      writeErrorPage(response, 'NOT_FOUND');
    }
    else {
      var stat = fs.statSync(file);
      // 請求路徑為目錄返回403頁面
      if(stat.isDirectory()) {
        writeErrorPage(response, 'FORBIDDEN');
      }
      else {
        response.writeHeader(200, {
          "Content-Type" : "text/html"
        });
        response.end(
          fs.readFileSync(file, 'utf-8')
        );
      }
    }
  });
}

將靜態資源請求的行為置后的設計,是為了保證回調函數一定能執行。當靜態資源不存在時,應當返回不存在的錯誤,同時也設置了禁止目錄的訪問的規則。

后話

現在,只是實現了WEB服務器基本的功能,它還有很大的改進空間。我將項目開源到github上,有興趣的可以克隆下來。


免責聲明!

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



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