nodejs-4

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值