由於想在項目中作一個 mock 數據服務器,目前,做爲項目中的 mock 數據服務器呢,還有幾個問題須要處理。因此先把目前僅僅 「Node.js 搭建本地文件服務器」 的源碼貼出來。javascript
訪問 http://localhost:3000/
顯示 mock/mockapi/
下的全部目錄以及文件;
訪問 http://localhost:3000/getUsersInfo.json
顯示 mock/mockapi/getUsersInfo.json
文件數據。css
// mock/index.js const http = require('http'); const processRequest = require('./server'); const port = 3000; const httpServer = http.createServer((req, res) => { processRequest(req, res) }) httpServer.listen(port, () => { console.log(`app is running at port: ${port}`); });
// mock/server.js const fs = require('fs') const url = require('url') const path = require('path') const mime = require('./mime') const headers = { 'Access-Control-Allow-Origin': '*', // 容許跨域 'Content-Type': 'text/plain' } const processRequest = (request, response) => { let pathName = url.parse(request.url).pathname // 防止中文亂碼 pathName = decodeURI(pathName) // 獲取資源文件的絕對路徑 let filePath = path.resolve(__dirname + '/mockapi/' + pathName) // 文件後綴名 let ext = path.extname(pathName) ext = ext ? ext.slice(1) : 'unknown' // 未知類型一概用 "text/plain" 類型 headers['Content-Type'] = mime[ext] || "'text/plain'" // 301重定向 if (!pathName.endsWith('/') && path.extname(pathName) === '') { pathName += '/' var redirect = 'http://' + request.headers.host + pathName response.writeHead(301, { location: redirect }) response.end() } fs.stat(filePath, (err, stats) => { // 未找到文件 if (err) { headers['Content-Type'] = 'text/html' response.writeHead(404, headers) response.end("<h1>404 Not Found</h1>") } // 文件 if (!err && stats.isFile()) { fs.readFile(filePath, (err, data) => { if (err) { response.writeHead(500, headers) response.end('<h1>500 Server Error</h1>') } response.writeHead(200, headers); response.end(data) }) } // 目錄 if (!err && stats.isDirectory()) { var html = '<head><meta charset="utf-8" /></head>' fs.readdir(filePath, (err, files) => { if (err) { html += `<div>讀取路徑失敗!</div>` response.writeHead(404, headers) response.end(html) } else { headers['Content-Type'] = 'text/html' response.writeHead(200, headers) for (var file of files) { if (file === 'index.html') { response.end(file) break } html += `<div><a href="${file}">${file}</a></div>` } response.end(html) } }) } }) } module.exports = processRequest
// mock/mime.js module.exports = { "css": "text/css", "gif": "image/gif", "html": "text/html", "ico": "image/x-icon", "jpeg": "image/jpeg", "jpg": "image/jpeg", "js": "text/javascript", "json": "application/json", "pdf": "application/pdf", "png": "image/png", "svg": "image/svg+xml", "swf": "application/x-shockwave-flash", "tiff": "image/tiff", "txt": "text/plain", "wav": "audio/x-wav", "wma": "audio/x-ms-wma", "wmv": "video/x-ms-wmv", "xml": "text/xml" };
http.createServer([options][, requestListener])
返回一個新得 http.Server
實例;http.Server
類 繼承自 net.Server
;net.Server
類 用於建立 TCP
或 IPC server
。html
server.listen(options[, callback])
開啓 HTTP 服務器監聽連接。java
mock/server.js
文件 把對 response
和 request
的處理封裝成一個匿名函數,傳入 http.createServer()
中。node
// mock/index.js const http = require('http'); const processRequest = require('./server'); const port = 3000; const httpServer = http.createServer((req, res) => { processRequest(req, res) }) httpServer.listen(port, () => { console.log(`app is running at port: ${port}`); })
// mock/server.js const url = require('url') const path = require('path') const processRequest = (request, response) => { let pathName = url.parse(request.url).pathname // 防止中文亂碼 pathName = decodeURI(pathName) // 獲取資源文件的絕對路徑 let filePath = path.resolve(__dirname + '/mockapi/' + pathName) }
url.parse()
請求 http://localhost:3000/api/users.json
地址,其中,request.url
爲 /api/users.json
;
請求 http://localhost:3000/api/users.json?a=1
地址,其中,request.url
爲 /api/users.json?a=1
。
因此這裏須要用到,url
模塊 解析URL。json
url
模塊提供了一些實用函數,用於 URL 處理與解析。url.parse()
方法會解析一個 URL 字符串並返回一個 URL 對象。api
下圖中,網址 http://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash
由 url.parse()
返回的對象的屬性。跨域
這裏,咱們能夠 pathName
即查找文件 相對於 mock/mockapi/
的路徑。瀏覽器
path.resolve()
有了 pathName
,如今就須要獲取資源文件的絕對路徑filePath
,方便以後的文件查找以及文件讀取。服務器
path
模塊提供了一些工具函數,用於處理文件與目錄的路徑。path.resolve([...paths])
方法會把一個路徑或路徑片斷的序列解析成一個絕對路徑。
例子:
path.resolve('/foo/bar', './baz'); // 返回 '/foo/bar/baz' path.resolve('/foo/bar', '/tmp/file/'); // 返回 '/tmp/file' path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif'); // 若是當前工做目錄爲 /home/myself/node, // 則返回 '/home/myself/node/wwwroot/static_files/gif/image.gif'
若是是訪問目錄文件,且未以 '/' 結尾,這裏作一下處理,重定向到 訪問路徑尾部添加 '/' 的地址。
// 301重定向 if (!pathName.endsWith('/') && path.extname(pathName) === '') { pathName += '/' var redirect = 'http://' + request.headers.host + pathName response.writeHead(301, { location: redirect }) response.end() }
後面在返回數據的時候,須要用到 response.writeHead(statusCode[, statusMessage][, headers])
方法,發送一個響應頭給請求。
狀態碼是一個三位數的 HTTP 狀態嗎,如404
。最後一個參數 headers
是響應頭。第二個參數 statusMessage
是可選的狀態描述。
這裏,須要設置響應頭的 Access-Control-Allow-Origin
和 Content-Type
。
Content-Type
,內容類型,通常是指網頁中存在的 Content-Type
,用於定義網絡文件的類型和網頁的編碼,決定瀏覽器將以什麼形式,什麼編碼讀取這個文件。
Access-Control-Allow-Origin: <orgin>
指定了該響應的資源是否被容許 與 給定的orgin
共享;*
做爲通配符,容許全部域都具備訪問資源的權限。
const headers = { 'Access-Control-Allow-Origin': '*', // 容許跨域 'Content-Type': 'text/plain' } const processRequest = (request, response) => { let pathName = url.parse(request.url).pathname // 防止中文亂碼 pathName = decodeURI(pathName) // 文件後綴名 let ext = path.extname(pathName) ext = ext ? ext.slice(1) : 'unknown' // 未知類型一概用 "text/plain" 類型 headers['Content-Type'] = mime[ext] || "'text/plain'" }
fs
模塊提供了一些 API,用於以一種相似標準 POSIX 函數的方式與文件系統進行交互。
fs.Stats
對象提供了一個文件的信息。能夠從 fs.stat()
返回。stats.isDirectory()
若是 fs.Stats 對象表示一個文件系統目錄,則返回 true 。stats.isFile()
若是 fs.Stats 對象表示一個普通文件,則返回 true 。
fs.readFile(path[, options], callback)
異步地讀取一個文件的所有內容。fs.readdir(path[, options], callback)
異步地讀取一個目錄的內容
fs.stat(filePath, (err, stats) => { // 未找到文件 if (err) { headers['Content-Type'] = 'text/html' response.writeHead(404, headers) response.end("<h1>404 Not Found</h1>") } // 文件 if (!err && stats.isFile()) { fs.readFile(filePath, (err, data) => { if (err) { response.writeHead(500, headers) response.end('<h1>500 Server Error</h1>') } response.writeHead(200, headers); response.end(data) }) } // 目錄 if (!err && stats.isDirectory()) { var html = '<head><meta charset="utf-8" /></head>' fs.readdir(filePath, (err, files) => { if (err) { html += `<div>讀取路徑失敗!</div>` response.writeHead(404, headers) response.end(html) } else { headers['Content-Type'] = 'text/html' response.writeHead(200, headers) for (var file of files) { if (file === 'index.html') { response.end(file) break } html += `<div><a href="${file}">${file}</a></div>` } response.end(html) } }) } })
response.end([data][, encoding][, callback])
該方法會通知服務器,全部響應頭和響應主題都已被髮送,即服務器將其視爲已完成。每次響應都必須調用 response.end()
方法。
若是指定了 data
,則至關於調用 response.write(data, encoding)
以後再調用 response.end(callback)
。
若是指定了 callback
,則當響應流結束時被調用。
response.writeHead(404, headers) response.end('<h1>500 Server Error</h1>')