nodejs封装一个类似express的路由
模块化封装
先看一下怎么进行模块化封装。将之前写在app.js中的路由抽离到module/routers.js中,然后进行封装
- module/routes
const fs = require('fs');
const path = require('path');
const url = require('url');
const ejs = require('ejs');
let getFileMime = function (extname) {
var data = fs.readFileSync('./data/mime.json'); // 同步方法读取内容(读取完后才会向下执行)
let mimeObj = JSON.parse(data.toString());
return mimeObj[extname];
}
let app = {
static: (req, res, staticPath) => {
let pathname = url.parse(req.url).pathname;
pathname = pathname === '/' ? '/index.html' : pathname;
let extname = path.extname(pathname);
if (pathname !== '/favicon.ico') {
try {
let data = fs.readFileSync('./' + staticPath + pathname);
if (data) {
let mime = getFileMime(extname);
res.writeHead(200, { 'Content-Type': '' + mime + ';charset="utf-8"' });
res.end(data);
}
} catch (error) {
console.log(error);
}
}
},
login: (req, res) => {
ejs.renderFile('./views/form.ejs', {}, (err, data) => {
res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
res.end(data);
})
},
news: (req, res) => {
res.end('news');
},
doLogin: (req, res) => {
// 获取post值
let postData = '';
req.on('data', (chunk) => {
postData += chunk;
})
req.on('end', () => {
console.log(postData);
res.end(postData);
})
},
error: (req, res) => {
res.end('error');
}
}
module.exports = app;
- app.js
const http = require('http');
const routes = require('./module/routes');
const url = require('url');
http.createServer(function (req, res) {
routes.static(req, res, 'static');
// 路由
let pathname = url.parse(req.url).pathname.replace('/', '');
try {
// 如果pathname存在就加载routes里面对应的pathname
routes[pathname](req, res);
} catch (error) {
// 如果pathname不存在就加载routes里面对应的error
routes['error'](req, res)
}
}).listen(3000);
console.log('Server running at http://127.0.0.1:3000/');
但是我这块运行之后一直在报一个错,不知道是什么原因😔
封装一个类似express框架的路由
正式开始…
正常express的使用就是和下面一样,先引入,然后实例化,然后通过app.get / app.post 来用
先看简单的静态路由调用
- 1_express_router.js
let app = function(){
console.log("hello world~");
}
app.get = function(){
console.log("get方法");
}
app.post = function(){
console.log("post方法");
}
// 调用
// app.get();
// app.post();
app();
再看加参数之后的路由调用
就是类似app.get('/login', function(req, res){});
这种调用方法来调用👇
- 2_express_router.js
let G = {}; // global的意思
let app = function(req, res){
// 在调用这个app方法的时候,想执行下面的这个方法的话
if(G['/login']){
G['/login'](req, res); // 执行方法,调用下面的 G[str] = callback;
}
}
app.get = function(str, callback){
// 注册方法
G[str] = callback;
/**
上面的这个 G[str] = callback 就是下面这个的意思
str就是路由,callback就是后面的回调函数
G['/login'] = function(req, res){
res.send("hellow world~");
});
*/
}
// 执行方法
// app.get(); 这是按照之前的方法嗲用的get,如果想要在调用的时候传参的话,就像下面这样
app.get('/login', function(req, res){
console.log("执行login方法");
});
setTimeout(() => {
app('req', 'res');
}, 1000);
再看结合http.createServer(结合nodejs中的web服务)实现类似配置的express的路由
get方法的封装
http.createServer是注册web服务,浏览器请求才会执行app(),然后node app.js启动程序后,app.get()等注册路由先执行,最后浏览器http请求时才会执行调用app
- 3_express_router.js
const http = require('http');
const app = require('./module/route.js')
// 注册web服务
// 这里的这个app的调用就相当于调用了route.js中的app方法
http.createServer(app).listen(3000);
// 相当于用 app 替换了 function (request, response) {}
// http.createServer(function (request, response) {}).listen(3000);
// 配置路由
app.get('/', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
res.end('首页');
})
app.get('/news', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
res.end('新闻');
})
app.get('/login', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
res.end('执行登录操作');
})
- route.js
const url = require('url');
let G = {};
let app = function(req, res){
let pathname = url.parse(req.url).pathname;
if(G[pathname]){
G[pathname](req, res);
}else{
res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' });
res.end('页面不存在');
}
}
app.get = function(str, callback){
// 注册方法
G[str] = callback;
}
module.exports = app;
post方法的封装
- app.js
const http = require('http');
const app = require('./module/route.js');
const ejs = require('ejs');
// 注册web服务
http.createServer(app).listen(4000);
// 配置路由
app.get('/', (req, res) => {
res.send('首页')
})
app.get('/news', (req, res) => {
res.send('新闻');
})
app.get('/login', (req, res) => {
ejs.renderFile("./views/form.ejs", {}, (err, data) => {
res.send(data)
})
})
app.post('/doLogin', (req, res) => {
// login 页面点击提交之后打印在dologin页面的数据
console.log(req.body);
res.send(req.body);
})
- route.js
const url = require('url');
function changeRes(res) {
res.send = (data) => {
res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
res.end(data);
}
}
let server = () => {
let G = {};
// 将get和post的请求分开。
// 这块分开是为了:当get/post的调用接口方法一样时,可以区分到底是get的还是post的
G._get = {};
G._post = {};
let app = function (req, res) {
// 扩展res的方法
changeRes(res);
let pathname = url.parse(req.url).pathname;
// 获取请求类型
let method = req.method.toLowerCase();
if (G['_' + method][pathname]) {
if (method === 'get') {
G['_' + method][pathname](req, res); // 执行方法
} else {
// post 请求时获取post数据并把它绑定到req.body中
let postData = '';
req.on('data', (chunk) => {
postData += chunk;
})
req.on('end', () => {
req.body = postData;
G['_' + method][pathname](req, res); // 执行方法
})
}
} else {
res.writeHead(404, {
'Content-Type': 'text/html;charset="utf-8"'
});
res.end('页面不存在');
}
}
app.get = function (str, callback) {
// 注册方法
G._get[str] = callback;
}
app.post = function (str, callback) {
// 注册方法
G._post[str] = callback;
}
return app;
}
module.exports = server();
- views/form.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="/doLogin" method="post">
用户名:<input type="text" name="username" />
密 码:<input type="text" name="password" />
<input type="submit" value="提交">
</form>
</body>
</html>
静态web服务的封装
为什么要封装静态web服务呢?
比如说在之前的这个get/post的demo中👆👆,在 view/form.ejs 中如果想引入一个css文件,这个时候如果不创建静态web服务的话,引入了这个css文件就用不了,👇
所以说,我们需要自己封装静态web服务,让我们自己封装的nodejs框架能访问自己的静态页面
- app.js
const http = require("http");
const app=require('./module/route');
const ejs = require("ejs");
//注册web服务
http.createServer(app).listen(4000);
app.static("static"); //修改默认静态web目录
//配置路由
app.get('/',function(req,res){
res.send("首页")
})
//配置路由
app.get('/login',function(req,res){
ejs.renderFile("./views/form.ejs",{},(err,data)=>{
res.send(data)
})
})
app.post('/doLogin',function(req,res){
console.log(req.body);
res.send(req.body)
})
- module/route.js
const fs = require('fs');
const path = require('path');
const url = require('url');
// 扩展res
function changeRes(res) {
res.send = (data) => {
res.writeHead(200, {
'Content-Type': 'text/html;charset="utf-8"'
});
res.end(data);
}
}
// 根据后缀名获取文件类型
function getFileMime(extname) {
var data = fs.readFileSync('./data/mime.json');
let mimeObj = JSON.parse(data.toString());
return mimeObj[extname];
}
// 静态web服务的方法
function initStatic(req, res, staticPath) {
// 1 获取地址
let pathname = url.parse(req.url).pathname;
// 这块可能需要诸事掉
pathname = pathname == '/' ? '/index.html' : pathname;
let extname = path.extname(pathname);
// 2 通过fs模块读取文件
if (pathname != '/favicon.ico') {
if (extname) {
try {
let data = fs.readFileSync('./' + staticPath + pathname);
if (data) {
let mime = getFileMime(extname);
res.writeHead(200, {
'Content-Type': '' + mime + ';charset="utf-8"'
});
res.end(data);
}
} catch (error) {
console.log(error);
}
}
}
}
let server = () => {
// G -> globe 全局变量
let G = {
_get: {},
_post: {},
staticPath: 'public' // 静态web目录
};
let app = function (req, res) {
// 扩展res的方法
changeRes(res);
// 配置静态web服务
initStatic(req, res, G.staticPath);
let pathname = url.parse(req.url).pathname;
// 获取请求类型
let method = req.method.toLowerCase();
let extname = path.extname(pathname);
if (!extname) {
if (G['_' + method][pathname]) {
if (method === 'get') {
G['_' + method][pathname](req, res); // 执行方法
} else {
// post 请求时获取post数据并把它绑定到req.body中
let postData = '';
req.on('data', (chunk) => {
postData += chunk;
})
req.on('end', () => {
req.body = postData;
G['_' + method][pathname](req, res); // 执行方法
})
}
} else {
res.writeHead(404, {
'Content-Type': 'text/html;charset="utf-8"'
});
res.end('页面不存在');
}
}
}
// 配置get请求
app.get = function (str, callback) {
// 注册方法
G._get[str] = callback;
}
// 配置post请求
app.post = function (str, callback) {
// 注册方法
G._post[str] = callback;
}
// 配置静态web服务目录
app.static = function (staticPath) {
G.staticPath = staticPath;
}
return app;
}
module.exports = server();
- views/form.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<form action="/doLogin" method="post">
<h2>登录页面</h2>
用户名:<input type="text" name="username" />
密 码:<input type="text" name="password" />
<input type="submit" value="提交">
</form>
</body>
</html>
- static/css/style.css
h2{
color: pink;
}