美文网首页前端开发那些事儿
原生node封装一个类似express的路由(一)

原生node封装一个类似express的路由(一)

作者: 踏莎行 | 来源:发表于2021-07-02 12:31 被阅读0次
    首先分析一下express的路由都有什么
    • 开放静态资源访问
    • 对get,post请求进行处理
    express是怎么实现的
    const express = require("express");
    const app = express();
    app.static("public")
    app.get('/get', (req, res) => {……})
    app.post("/post", (req, res) => {……})
    

    先来实现对get以及post请求的处理

    1.先定义一个route.js的文件,说白了就是定义一个路由模块
    首先在app.js中通过http模块开启一个服务,监听的8081端口

    var http = require('http');
    http.createServer(function (request, response) {
      response.writeHead(200, {'Content-Type': 'text/plain'});
      response.end('Hello World');
    }).listen(8081);
    

    createServer函数中传入一个回调函数,回调函数有两个参数request存有客户端发来的请求信息,response服务器对客户端的响应,在函数内部通过获取不同的request.url请求路径进行不同的处理和响应,所以我们在route.js中暴露一个app函数,统一处理请求,那么http服务就可以直接写成

    const app = require('./route')
    http.createServer(app).listen(8081);
    

    在route.js中定义一个函数server,返回一个app函数,这样将变量定义在函数作用域防止污染全局。暴露server执行,返回的就是app这个函数了
    route.js

    let server = () => {
      let app = function (req, res) {
      }
      return app
    }
    
    module.exports = server()
    

    2.处理get,post请求
    因为在app.js中注册路由的是app.get/app.post方式的方法,所以此时在app这个函数上定义两个方法,不同的请求方法式处理不同,在server函数中再丁一一个对象G,对象G再定义两个对象分别存储定义的函数

    Snipaste_2021-06-27_15-52-56.png
    let server = () => {
      let G = {
        _post: {},
        _get: {}
      }
    
      let app = function (req, res) {
      }
    
      app.get = function (str, cb) {
        G._get[str] = cb
      }
    
      app.post = function (str, cb) {
        G._post[str] = cb
      }
      return app
    }
    

    以app.get()方法为例,我们将app暴露出去,在app.js中引入了这个模块,所以在app.js中就可以调用app.get()方法了,因为是要处理路由,所以app.get函数的第一个参数就是路由,第二个参数就是传入对应的回调函数来处理这个路由的逻辑。app.get方法内部就会对该回调函数进行注册,将str作为_get对象的属性,回调函数就是属性值了。
    现在进行一次测试
    app.js
    这里提前定义一个/login的路由,然后执行app.js文件,在浏览器输入"http://localhost:8081/login",如果页面有“home”打印出来就证明成功了。

    const app = require('./route')
    
    var http = require('http');
    http.createServer(app).listen(8081);
    
    app.get('/login', (req, res) => {
      res.end("home")
    })
    

    route.js
    因为这里的app函数是作为http.createServer的回调函数,我们不用手动执行,它自己就会执行。在测试的时候事先注册了/login函数,所以在里面判断一下G的_get对象里面有没有这个函数,如果有就执行,所以他一执行就会执行我们为'/login'路由注册的函数,就会给客户端返回字符串“home”

    let server = () => {
      let G = {
        _post: {},
        _get: {
        }
      }
    
      let app = function (req, res) {
        if(G._get['/login']){
          G._get['/login'](req, res)
        }
      }
    
      app.get = function (str, cb) {
        G._get[str] = cb
      }
    
      app.post = function (str, cb) {
        G._post[str] = cb
      }
      return app
    }
    
    module.exports = server()
    

    3.实现route.js的app函数的功能
    客户端发来的请求可能是post可能是get,而post和get的路由在定义的时候也可能是一样的

    app.get('/login', (req, res) => {
      res.end("home")
    })
    
    app.post('/login', (req, res) => {
      res.end("home")
    })
    

    所以要根据请求方式和路由名称来决定执行哪一个方法

    • 首先获取请求的路由名称和请求方法
      利用url模块的parse方法获取请求路径中的路由
    const url = require('url')
    
    let app = function (req, res) {
        // 获取请求的路由,就是如http://localhost:8081/login中的/login
        let pathname = url.parse(req.url).pathname;
        // 获取请求方法
        let method = req.method.toLowerCase();
    }
    
    • 根据路由和请求方法决定执行哪个处理函数
    let app = function (req, res) {
        // 获取请求的路由
        let pathname = url.parse(req.url).pathname;
        // 获取请求方法,返回的是大写的,这里转成小写了
        let method = req.method.toLowerCase();
        
        // 先判断G的_get和_post对象里面有没有对应的路由,如果有向下执行,没有走else
        if (G['_' + method][pathname]) {
          // 判断请求方法
          if (method == "get") {
            // get请求方法,执行G._get对象中对应的pathname方法,需要把req和res传过去
            G['_' + method][pathname](req, res);  //执行方法
          } else {
            // post方法,post请求参数是以流的形式传递,是一点一点的传输的
            let postData = '';
            // post参数传输中
            req.on('data', (chunk) => {
              postData += chunk;
            })
            // post参数传输完毕,将参数绑定到req.body上,执行G._post对象中对应的函数
            req.on('end', () => {
              req.body = postData;
              G['_' + method][pathname](req, res);  //执行方法
            })
          }
        } else {
          // 设置响应头,状态码404,文件类型是html,编码字符集是utf-8
          res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' });
          // 提示页面不存在
          res.end('页面不存在');
        }
    }
    

    这样就算初步完成了
    再在res上扩展一个send方法,用来统一设置响应头和响应数据,封装一个changeRes方法,参数传入res

    function changeRes(res) {
      res.send = (data) => {
        res.writeHead(200, {
          "Content-Type": "text/html;charset=utf-8"
        })
    
        res.end(data)
      }
    }
    

    在app函数中直接调用一下changeRes方法就行了,传入res

    let app = function (req, res) {
        //扩展res的方法
        changeRes(res);
    }
    

    然后在注册路由时候,就可以直接使用了res.send()了,就不用另外再单独设置响应头了

    app.get('/login', (req, res) => {
      res.send("home")
    })
    

    下一节

    完整的router.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 = {
        _post: {},
        _get: {
        }
      }
    
      const 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 {
            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, cb) {
        G._get[str] = cb
      }
    
      app.post = function (str, cb) {
        G._post[str] = cb
      }
      return app
    }
    
    module.exports = server()
    

    相关文章

      网友评论

        本文标题:原生node封装一个类似express的路由(一)

        本文链接:https://www.haomeiwen.com/subject/rkzayltx.html