原生node封裝一個(gè)類似express的路由(一)

首先分析一下express的路由都有什么
  • 開放靜態(tài)資源訪問
  • 對get,post請求進(jìn)行處理
express是怎么實(shí)現(xiàn)的
const express = require("express");
const app = express();
app.static("public")
app.get('/get', (req, res) => {……})
app.post("/post", (req, res) => {……})

先來實(shí)現(xiàn)對get以及post請求的處理

1.先定義一個(gè)route.js的文件,說白了就是定義一個(gè)路由模塊
首先在app.js中通過http模塊開啟一個(gè)服務(wù),監(jiān)聽的8081端口

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

createServer函數(shù)中傳入一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)有兩個(gè)參數(shù)request存有客戶端發(fā)來的請求信息,response服務(wù)器對客戶端的響應(yīng),在函數(shù)內(nèi)部通過獲取不同的request.url請求路徑進(jìn)行不同的處理和響應(yīng),所以我們在route.js中暴露一個(gè)app函數(shù),統(tǒng)一處理請求,那么http服務(wù)就可以直接寫成

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

在route.js中定義一個(gè)函數(shù)server,返回一個(gè)app函數(shù),這樣將變量定義在函數(shù)作用域防止污染全局。暴露server執(zhí)行,返回的就是app這個(gè)函數(shù)了
route.js

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

module.exports = server()

2.處理get,post請求
因?yàn)樵赼pp.js中注冊路由的是app.get/app.post方式的方法,所以此時(shí)在app這個(gè)函數(shù)上定義兩個(gè)方法,不同的請求方法式處理不同,在server函數(shù)中再丁一一個(gè)對象G,對象G再定義兩個(gè)對象分別存儲定義的函數(shù)

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中引入了這個(gè)模塊,所以在app.js中就可以調(diào)用app.get()方法了,因?yàn)槭且幚砺酚?,所以app.get函數(shù)的第一個(gè)參數(shù)就是路由,第二個(gè)參數(shù)就是傳入對應(yīng)的回調(diào)函數(shù)來處理這個(gè)路由的邏輯。app.get方法內(nèi)部就會對該回調(diào)函數(shù)進(jìn)行注冊,將str作為_get對象的屬性,回調(diào)函數(shù)就是屬性值了。
現(xiàn)在進(jìn)行一次測試
app.js
這里提前定義一個(gè)/login的路由,然后執(zhí)行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
因?yàn)檫@里的app函數(shù)是作為http.createServer的回調(diào)函數(shù),我們不用手動(dòng)執(zhí)行,它自己就會執(zhí)行。在測試的時(shí)候事先注冊了/login函數(shù),所以在里面判斷一下G的_get對象里面有沒有這個(gè)函數(shù),如果有就執(zhí)行,所以他一執(zhí)行就會執(zhí)行我們?yōu)?/login'路由注冊的函數(shù),就會給客戶端返回字符串“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.實(shí)現(xiàn)route.js的app函數(shù)的功能
客戶端發(fā)來的請求可能是post可能是get,而post和get的路由在定義的時(shí)候也可能是一樣的

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

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

所以要根據(jù)請求方式和路由名稱來決定執(zhí)行哪一個(gè)方法

  • 首先獲取請求的路由名稱和請求方法
    利用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();
}
  • 根據(jù)路由和請求方法決定執(zhí)行哪個(gè)處理函數(shù)
let app = function (req, res) {
    // 獲取請求的路由
    let pathname = url.parse(req.url).pathname;
    // 獲取請求方法,返回的是大寫的,這里轉(zhuǎn)成小寫了
    let method = req.method.toLowerCase();
    
    // 先判斷G的_get和_post對象里面有沒有對應(yīng)的路由,如果有向下執(zhí)行,沒有走else
    if (G['_' + method][pathname]) {
      // 判斷請求方法
      if (method == "get") {
        // get請求方法,執(zhí)行G._get對象中對應(yīng)的pathname方法,需要把req和res傳過去
        G['_' + method][pathname](req, res);  //執(zhí)行方法
      } else {
        // post方法,post請求參數(shù)是以流的形式傳遞,是一點(diǎn)一點(diǎn)的傳輸?shù)?        let postData = '';
        // post參數(shù)傳輸中
        req.on('data', (chunk) => {
          postData += chunk;
        })
        // post參數(shù)傳輸完畢,將參數(shù)綁定到req.body上,執(zhí)行G._post對象中對應(yīng)的函數(shù)
        req.on('end', () => {
          req.body = postData;
          G['_' + method][pathname](req, res);  //執(zhí)行方法
        })
      }
    } else {
      // 設(shè)置響應(yīng)頭,狀態(tài)碼404,文件類型是html,編碼字符集是utf-8
      res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' });
      // 提示頁面不存在
      res.end('頁面不存在');
    }
}

這樣就算初步完成了
再在res上擴(kuò)展一個(gè)send方法,用來統(tǒng)一設(shè)置響應(yīng)頭和響應(yīng)數(shù)據(jù),封裝一個(gè)changeRes方法,參數(shù)傳入res

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

    res.end(data)
  }
}

在app函數(shù)中直接調(diào)用一下changeRes方法就行了,傳入res

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

然后在注冊路由時(shí)候,就可以直接使用了res.send()了,就不用另外再單獨(dú)設(shè)置響應(yīng)頭了

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

下一節(jié)

完整的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) {
    //擴(kuò)展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);  //執(zhí)行方法
      } 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()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容