美文网首页前端开发那些事儿
原生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