原生NodeJS封装Express路由
Express:https://www.expressjs.com.cn/
express路由:
var express = require('express') var app = express() app.get("/", function (req, res) { res.send('hello world') }) app.get("/login", function (req, res) { res.send('hello world') }) app.post("/doLogin", function (req, res) { res.send('POST request to the homepage') })
考虑定义一个 Router 模块,Router 模块中绑定一个 get 方法配置路由和一个 post 方法配置路由,以及一个 error 方法处理路由出错情况,并向外暴露 Router 模块,外部调用这个模块时,只需要传入一个路由和路由回调执行路由逻辑。
在 get 方法和post 方法中,首先需要在全局变量 Global 中注册一个路由,把我们外部传入的路由和路由回调绑定到 Global 中,在调用Router 模块时,就可以先判断是否这个路由是否注册了,没有注册,就执行 Router的 error 方法报 404,注册了就执行这个路由的路由回调逻辑。
const url = require('url') const Global = { }
const Router = function (req, res) { const pathname = url.parse(req.url).pathname if (Global[pathname]) { //如果是注册过的路由,就执行路由回调 Global[pathname](req, res) } else { Router.error(req, res) } } //给Router绑定get方法配置路由 Router.get = function (str, callback) { Global[str] = callback;//注册路由 }
//给Router绑定post方法配置路由 Router.post = function (str, callback) { Global[str] = callback;//注册路由 }
//给Router绑定error方法,路由未注册时报404 Router.error = function (req, res) { res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' }); res.end("404 error") } //将Router模块向外暴露出去 module.exports = Router;
外部调用:
Router.get('/login',function(req,res){ res.end('login') })
以上的Router模块 以及 Global 全部暴露在全局中,容易污染全局,所以可以将Router 封装到一个 Request 模块中,Request 模块的返回值就是 Router配置方法。
get路由注册和 post路由注册需要有区别,否则 Global [pathname] 都一样,post 路由配置会覆盖 get 路由配置,区分方式是在 Global 中绑定 _get 和 _post 两个属性,在注册路由时,如果是 get类型就注册 Global._get 路由,是 post类型,就注册 Global._post 路由
Global 中的 _get 和 _post 两种路由的执行,在执行之前先判断 reg的请求类型,拿到 reg.method,根据这个执行不同的路由回调,是 get 类型,直接执行 _get 路由逻辑,是 post 类型,可以拿到 post 请求中的参数,将这个参数放在 req.body 中,在执行 _post 的路由逻辑。
当然,路由没有注册( pathname不匹配 ),就执行路由错误报404
最后返回封装的路由方法给 Request 模块。
const url = require('url') const Request = function(){ const Global = { "_get":{ }, //把get和post分开 "_post":{ }, } const Router = function (req, res) { const pathname = url.parse(req.url).pathname const method = req.method.toLowerCase() //获取请求类型 get/post if (Global['_'+method][pathname]) { //拿到请求类型,直接通过Global执行相应类型的请求 if(method=='get'){ Global['_get'][pathname](req, res) } else if(method=='post'){ let params = ''; req.on('data',(chunk)=>{ params+=chunk; }) req.on('end',()=>{ req.body=params Global['_post'][pathname](req,res) }) } } else { Router.error(req, res) } } //给Router绑定get方法配置路由 Router.get = function (str, callback) { Global['_get'][str] = callback;//注册路由 } //给Router绑定post方法配置路由 Router.post = function (str, callback) { Global['_post'][str] = callback;//注册路由 } //给Router绑定error方法,路由未注册时报404 Router.error = function (req, res) { res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' }); res.end("404 error") } return Router; } //将Request模块向外暴露出去 module.exports = Request();
外部调用这个Request模块的路由配置方法,配置路由:
const http = require('http'); const ejs = require('ejs') const Request = require('./Router02') //注册web服务 http.createServer(Request).listen(8081); //配置路由 Request.get('/', function (req, res) { res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' }); res.end('<h3>这是首页</h3>'); }) Request.get('/login',function(req,res){ ejs.renderFile('./views/form.ejs', {}, (err, data) => { res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' }); res.end(data); }) }) Request.post('/doLogin', function (req, res) { res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' }); console.log(req.body) res.end(req.body); }) Request.get('/register',function(req,res){ res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' }); res.end('<h3>这是注册页</h3>'); }) console.log('Server running at http://127.0.0.1:8081/');
路由配置正常:
下面将响应信息封装成一个 send() 方法:
在 Router中可以修改 res ,扩展 res的方法:
Router 中可以封装一个静态web服务,这样可以既直接访问静态资源,也可以执行后端路由逻辑:
Router.js
const fs = require('fs') const path = require('path') const url = require('url') //web静态服务 function staticWeb(req,res,staticPath){ let pathname = url.parse(req.url).pathname //先获取地址 pathname = pathname == '/' ? '/index.html' : pathname //根目录下定位到首页 let ext = path.extname(pathname) //获取文件后缀名 let getMime = function (ext) { //获取文件类型 let data = fs.readFileSync('./mime.json'); //同步方法,没有回调 let mime = JSON.parse(data.toString())[ext] return mime; } try { let data = fs.readFileSync(staticPath + pathname) if (data) { let mime = getMime(ext) //获取文件类型 res.writeHead(200, { 'Content-Type': `${mime};charset="utf-8"` }); res.end(data); } } catch (error) { console.log("静态资源没有匹配,执行匹配后端路由") } } const Request = function(){ const Global = { "_get":{ }, //把get和post分开 "_post":{ }, staticPath:'./static' //设置默认静态web目录 } const Router = function (req, res) { const pathname = url.parse(req.url).pathname //获取访问的路由 const method = req.method.toLowerCase() //获取请求类型 get/post //给 res 扩展一个send()方法响应请求 res.send=function(data){ res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' }); res.end(data); } //配置静态web服务 staticWeb(req,res,Global.staticPath) if (Global['_'+method][pathname]) { //拿到请求类型,直接通过Global执行相应类型的请求 if(method=='get'){ Global['_get'][pathname](req, res) } else if(method=='post'){ let params = ''; req.on('data',(chunk)=>{ params+=chunk; }) req.on('end',()=>{ req.body=params Global['_post'][pathname](req,res) }) } } else { Router.error(req, res) } } //给Router绑定get方法配置路由 Router.get = function (str, callback) { Global['_get'][str] = callback;//注册路由 } //给Router绑定post方法配置路由 Router.post = function (str, callback) { Global['_post'][str] = callback;//注册路由 } //给Router绑定error方法,路由未注册时报404 Router.error = function (req, res) { res.send("404 error") } //配置静态web服务,传入一个静态目录地址即可,不传的话取Global中的默认路径 Router.static = function(staticPath){ Global.staticPath=staticPath } return Router; } //将Request模块向外暴露出去 module.exports = Request();
app.js
const http = require('http'); const ejs = require('ejs') const Request = require('./Router04') //注册web服务 http.createServer(Request).listen(8081); //配置路由 Request.get('/', function (req, res) { res.send('<h3>这是首页</h3>'); }) Request.get('/login',function(req,res){ ejs.renderFile('./views/form.ejs', {}, (err, data) => { res.send(data); }) }) Request.post('/doLogin', function (req, res) { res.send(req.body); }) Request.get('/register',function(req,res){ res.send('<h3>这是注册页</h3>'); }) console.log('Server running at http://127.0.0.1:8081/');
静态资源访问正常,后端路由也执行正常: