const fs = require('fs'); const url = require('url'); const path = require('path'); const http = require('http'); const crypto = require('crypto'); const xml2js = require('xml2js'); const config = {}; // 常见静态文件格式 const mime = { "html": "text/html", "css": "text/css", "js": "text/javascript", "json": "application/json", "gif": "image/gif", "ico": "image/x-icon", "jpeg": "image/jpeg", "jpg": "image/jpeg", "png": "image/png" } const app = {}; app.routes = []; let _static = '.'; // 命令集合 const methods = ['get', 'post', 'put', 'delete', 'options', 'all', 'use']; // 实现路由池 methods.forEach((method) => { app[method] = (path, fn) => { app.routes.push({method, path, fn}); } }); // 使用generator函数实现惰性求值 const lazy = function* (arr) { yield* arr; } // 遍历路由池 // routes 路由池 // method 命令 // path 请求路径 const passRouter = (routes, method, path) => (req, res) => { // 模式匹配 const replaceParams = (path) => new RegExp(`\^${path.replace(/:\w[^\/]+/g, '\\w[^\/]+')}\$`); const lazyRoutes = lazy(routes); (function next() { // 当前遍历状态 const it = lazyRoutes.next().value; if (!it) { // 已经遍历了所有路由 停止遍历 res.end(`Cannot ${method} ${path}`); return; } else if (it.method === 'use' && (it.path === '/' || it.path === path || path.startsWith(it.path.concat('/')))) { // 匹配到中间件 it.fn(req, res, next); } else if ((it.method === method || it.method === 'all') && (it.path === path || it.path === "*")) { if (it.method === "get") { // 获取query数据 获取get请求参数 req.query = url.parse(req.url, true).query; // 匹配到路由 it.fn(req, res); } else if (it.method === "post") { // 获取body数据 获取post请求参数 let info = ''; req.addListener('data', (chunk) => { info += chunk }).addListener('end', () => { req.body = JSON.parse(info); // 匹配到路由 it.fn(req, res); }) } } else if (it.path.includes(':') && (it.method === method || it.method === 'all') && (replaceParams(it.path).test(path))) { // 模式匹配 let index = 0; const params2Array = it.path.split('/'); // 注册函数的path const path2Array = path.split('/');// 请求路径的path const params = {}; params2Array.forEach((path) => { if (/\:/.test(path)) { // 如果是模式匹配的路径 就加入params对象中 params[path.slice(1)] = path2Array[index]; } index++ }) req.params = params; it.fn(req, res); } else if (it.method === 'get' && it.path.includes(req.url)) { // 若允许访问的目录的子目录未允许访问 res.writeHead(403, {'content-type': 'text/plain;charset=utf-8'}); res.end(`暂未有访问权限`); return; } else { // 继续匹配 next(); } })() } // 处理静态文件函数 // res response对象 // pathName 静态文件相对路径 // ext 静态文件后缀 function handleStatic(res, pathName, ext) { fs.exists(pathName, (exists) => { if (!exists) { res.writeHead(404, {'Content-Type': 'text/plain'}); res.write('The request url ' + pathName + ' was not found on this server'); res.end(); } else { fs.readFile(pathName, (err, file) => { if (err) { res.writeHead(500, {'Cotent-Type': 'text/plain'}); res.end(err); } else { // etag用于检验文件是否有变动 const etag = crypto.createHash('md5').update(file).digest('hex'); // md5算法 if (res.ifNoneMatch === etag) { res.writeHead(304); res.end() } else { const ContentType = mime[ext] || 'text/plain'; res.setHeader('Etag', etag); res.writeHead(200, {'Cotent-Type': ContentType}); res.write(file); res.end(); } } }) } }) } // 目录浏览 // dir 需要提供目录浏览功能的目录 // dirname 本地路径 即__dirname // bool 子目录是否提供浏览功能 app.dir = function (_dir, dirname, bool) { app.get(_dir, (req, res) => { let html = "
"; try { // 用户访问目录 let files = fs.readdirSync(dirname + _dir); let fileName = null; for (let i in files) { if (path.extname(files[i]) === "" && bool === true) { app.dir(_dir + '/' + files[i], dirname, bool) } fileName = files[i]; html += ""; } } catch (e) { html += '