天天看點

靜态檔案伺服器擴容,node靜态檔案伺服器端實作與擴充

以前剛開始學node的時候寫過幾個小項目練手,但都是一開始就用到了express架構,這段時間重學node基礎,想到如何用node實作一個類似于express的架構,于是就想從靜态檔案伺服器開始實作一部分功能,實作了基本的檔案伺服器後我還繼續擴充了路由邏輯處理的功能,用于簡易伺服器背景的實作與搭建

一、建立伺服器

利用http子產品的createServer建立一個伺服器,就是這麼簡單, 如下為app.js啟動檔案的配置

const config = require('./config');

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

const handle = require('./handle');

const http = require('http');

var httpServer = http.createServer(onRequest);

httpServer.listen(config.port || 8888);

httpServer.on('error', (err) => {

console.log(err);

});

function onRequest(request, response) {

console.log('request received');

router(request, response, handle);

}

.config為配置檔案,用于導出配置資訊,便于配置資訊的統一修改,定義了伺服器的端口位置,和支援的MINE類型(可自行擴充)

module.exports = {

port: 8888,

ip: '127.0.0.1',

mine: {

html: 'text/html',

js: 'text/javascript',

css: 'text/css',

gif: 'image/gif',

jpg: 'image/jpeg',

png: 'image/png',

ico: 'image/icon',

txt: 'text/plain',

json: 'application/json',

xml: 'text/xml',

pdf: 'application/pdf',

default: 'application/octet-stream'

}

};

二、路由的實作

路由子產品主要用于解析請求URL中的路徑,并對不同的路徑實施不用的處理邏輯。

用到的子產品:url 解析請求URL傳回Object對象

path 處理路徑

首先,我們先要實作一個根據path擷取MINE類型的方法

var getFileMine = (pathname) => {

let extname = path.extname(pathname).substr(1);

let mineType = config.mine;

if (mineType.hasOwnProperty(extname)) {

return mineType[extname];

} else {

return false;

}

};

核心處理邏輯就是,首先檢查請求URL的檔案MINE類型,如果是我們配置檔案config.js 中定義的類型之一,則調用routerHandle子產品(稍後會講)的檔案讀取功能去讀取并傳回對應的請求檔案;若URL沒有MINE類型的字尾名,則預設為調用路由處理方法,根據handle.js中配置的handle對象的方法處理;若在handle對象中沒有對應的路由處理方法,則傳回404 Not Found.

完整的實作代碼為:

const url = require('url');

const path = require('path');

const routerHandle = require('./routerHandle');

const config = require('../config');

module.exports = (function() {

var getFileMine = (pathname) => {

let extname = path.extname(pathname).substr(1);

let mineType = config.mine;

if (mineType.hasOwnProperty(extname)) {

return mineType[extname];

} else {

return false;

}

};

return function(request, response, handle) {

let pathname = url.parse(request.url).pathname;

let mine = getFileMine(pathname);

pathname = decodeURI(pathname);

if(mine) {

routerHandle.static(request, response, pathname, mine);

} else if (typeof handle[pathname] === 'function') {

handle[pathname](request, response);

} else {

response.writeHead(404, {'Content-Type': 'text/plain'});

response.write('404 Not Found');

response.end();

}

};

})();

三、路由handle的實作

最後就是對于不同的路由調用不用等handle來處理了,這裡我在routerHandle.js中定義了一個static方法專門用來處理靜态檔案的讀取傳回操作

exports.static = (request, response, pathname, mine) => {

pathname = path.join('./static', path.normalize(pathname.replace(/\.\./g, '')));

let promise = new Promise((resolve, reject) => {

fs.exists(pathname, (exists) => {

if(!exists) {

reject();

} else {

resolve();

}

});

});

promise.then(() => {

response.writeHead(200, {'Content-Type': mine});

let readStream = fs.createReadStream(pathname);

readStream.on('error', (err) => {

serverException(response); //自定義方法,處理500錯誤

});

readStream.pipe(response);

}).catch(() => {

notFoundException(response); //自定義方法,處理404錯誤

});

};

然後,為了提高相容性,我對于 '/' 路徑的請求做了一個重定向處理,預設通路 ./static 檔案夾下的index.html檔案

exports.start = (request, response) => {

let redirect = 'http://' + request.headers.host + '/index.html';

response.writeHead(301, { 'location': redirect});

response.end();

};

至此,一個基本的靜态檔案伺服器就已經實作了。

對于,自定義路由,在handle.js檔案中require自己建立的路由檔案(建議放在router檔案夾下),并通過handle對象注冊自定義路由檔案中自定義的功能(如:handle['/test'] = myRouter.myTest;)

const routerHandle = require('./router/routerHandle');

const myRouter = require('./router/myRouter');

// 1. 添加自定義子產品

let handle = {};

handle['/'] = routerHandle.start;

// 2. 為自定義子產品定義路由

handle['/test'] = myRouter.myTest;

module.exports = handle;

以上代碼,我建立了一個myRouter.js檔案用于放置自定義的路由方法,并通過handle對象擴充這一路由檔案下的myTest方法,如果要添加其他方法,可以效仿。