咱們知道最原生的處理http請求的服務端應該這樣寫node
const http = require("http")
const server = http.createServer(function (req,res) {
console.log(req)
res.setHeader("Content-Type","text/plain")
res.write("hello world")
console.log(res)
res.end
})
server.listen(3000)
複製代碼
而後保存爲test.js
,用 node --inspect test.js
運行,在chrome://inspect/#devices
打開調試界面調試界面,而後訪問 http://localhost:3000/aa/bb?qq=ww
,在調試界面查看結果.chrome
這應該就是最簡單的node http server端的代碼了。 咱們首先建立了一個server,並給他傳入了一個回調函數,而後讓他監聽3000 端口。express
這個回調函數接受兩個參數,req:包含http請求的相關信息;res:即將返回的http相應的相關信息。json
當咱們接收到以個http請求後,咱們最關注哪些信息? 通常比較關注的有:數組
等信息。可是這些信息在req中太原始了,瀏覽器
req.url:"/aa/bb?qq=ww"
req.headers.cookie
因此咱們在接收一個請求可先作一些處理,好比說先將查詢字符串和cookie從字符串parse爲鍵值對,而後再進入業務邏輯。 咱們能夠這樣寫:bash
const http = require("http")
const server = http.createServer(function (req,res) {
getQueryObj(req)
getCookieObj(req)
res.setHeader("Content-Type","text/plain")
res.write(JSON.stringify(req.query))
res.write(JSON.stringify(req.cookie))
res.end()
})
server.listen(3000)
function getQueryObj(req){
let query = {}
let queryString = req.url.split("?")[1] || ""
let items = queryString.length ? queryString.split("&") : []
for (let i=0; i < items.length; i++){
let item = items[i].split("=");
let name = decodeURIComponent(item[0]);
let value = decodeURIComponent(item[1]);
query[name] = value;
}
req.query = query
}
function getCookieObj(req) {
let cookieString = req.headers.cookie || ""
let cookieObj = {}
let items = cookieString.length ? cookieString.split(";") : []
for (let i=0; i < items.length; i++){
let item = items[i].split("=");
let name = decodeURIComponent(item[0]);
let value = decodeURIComponent(item[1]);
cookieObj[name] = value;
}
req.cookie = cookieObj
}
複製代碼
咱們看到,咱們確實將查詢字符串和cookie提取出來了,已備後續使用。cookie
上述代碼確實完成了任務,可是有很是明顯的缺陷---代碼耦合度過高,不便於維護。此次寫個函數處理查詢字符串,下次寫個函數處理cookie,那再下次呢。app
每添加一個函數就要修改callback,很是不便於維護。函數
那麼咱們能夠怎樣修改呢?
咱們能夠聲明一個函數數組afterReqArrayFuns
,而後在callback函數中寫道
afterReqArrayFuns.forEach(fun => {
fun(req)
})
複製代碼
這樣能夠代碼自動適應變化,咱們每寫一個函數,就將他push到這個數組,就能夠了。
這是代碼:
const http = require("http")
const myHttp = {
listen:function(port){
const server = http.createServer(this.getCallbackFun())
return server.listen(port)
},
getCallbackFun:function(){
let that = this
return function (req,res) {
that.afterReqArrayFuns.forEach(fun => {
fun(req)
})
res.write(JSON.stringify(req.query))
res.end()
}
},
afterReqArrayFuns:[],
afterReq:function(fun){
this.afterReqArrayFuns.push(fun)
}
}
function getQueryObj(req){
//同上
}
function getCookieObj(req) {
//同上
}
myHttp.afterReq(getQueryObj)
myHttp.afterReq(getCookieObj)
myHttp.listen(3003)
複製代碼
除了預處理http請求,咱們另外一個要求就是對不一樣的請求url作出正確的迴應,在express
中,這種寫法很舒服:
const express = require('express');
const app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.post('/aa', function (req, res) {
res.send('Hello World!');
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
複製代碼
每一個url對應一個路由函數
可是在原生的http中,咱們可能要寫無數個if-else
或則 switch-case
. 那麼咱們怎麼實現相似express的寫法呢?
咱們能夠創建一個url-callback的map對象,每次匹配到相應的url,變調用相應的回調函數。
看代碼
const http = require("http")
const myHttp = {
listen:function(port){
const server = http.createServer(this.getCallbackFun())
return server.listen(port)
},
getCallbackFun:function(){
let that = this
return function (req,res) {
that.afterReqArrayFuns.forEach(fun => {
fun(req)
})
let path = req.url.split("?")[0]
let callback = that.router[req.method][path] || 1 // !!!! look here !!!!!!
callback(req,res)
res.end()
}
},
afterReqArrayFuns:[],
afterReq:function(fun){
this.afterReqArrayFuns.push(fun)
},
router:{
"GET":{},
"POST":{},
},
get:function (path,callback) {
this.router["GET"][path] = callback
},
post:function(path,callback){
this.router["POST"][path] = callback
}
}
myHttp.get("/",(req,res) => {
res.write("it is /")
})
myHttp.get("/aa/bb",(req,res) => {
res.setHeader("Content-Type","text/plain")
res.write("it is /aa/bb")
})
myHttp.listen(3003)
複製代碼
業務邏輯中,callback函數並無寫死,而是動態肯定的
每次寫下myHttp.get(path,callback)後,都會在myHttp.router的創建鍵值對, 而在接受http請求後,模塊會查找對應的路由函數來處理請求。
上面的代碼看起來不規範,咱們用ES6語法來改寫
在module.js
const http = require("http");
class fishHttp {
constructor(){
this.afterReqArrayFuns = [];
this.router = {
"GET":{},
"POST":{},
}
}
listen(port){
const server = http.createServer(this.getCallbackFun());
return server.listen(port)
}
getCallbackFun(req,res){
let that =this;
return function (req,res) {
that.afterReqArrayFuns.forEach(fun => {
fun(req)
});
res.write(JSON.stringify(req.query));
let path = req.url.split("?")[0];
let callback = that.router[req.method][path] || that.NotExistUrl;
callback(req,res);
}
}
afterReq(fun){
for(let i = 0;i<arguments.length;i++){
this.afterReqArrayFuns.push(arguments[i])
}
}
get(path,callback) {
this.router["GET"][path] = callback
}
post(path,callback){
this.router["POST"][path] = callback
}
NotExistUrl(req,res){
res.end('Not found')
}
}
module.exports = fishHttp;
複製代碼
在同級目錄下test.js
const fishHttp = require("./module") //node 自動嘗試.js .node .json擴展名
function getQueryObj(req){
let query = {}
let queryString = req.url.split("?")[1] || ""
let items = queryString.length ? queryString.split("&") : []
for (let i=0; i < items.length; i++){
let item = items[i].split("=");
let name = decodeURIComponent(item[0]);
let value = decodeURIComponent(item[1]);
query[name] = value;
}
req.query = query
}
function getCookieObj(req) {
let cookieString = req.headers.cookie || ""
let cookieObj = {}
let items = cookieString.length ? cookieString.split(";") : []
for (let i=0; i < items.length; i++){
let item = items[i].split("=");
let name = decodeURIComponent(item[0]);
let value = decodeURIComponent(item[1]);
cookieObj[name] = value;
}
req.cookie = cookieObj
}
myHttp = new fishHttp()
myHttp.afterReq(getQueryObj,getCookieObj)
myHttp.get("/",(req,res) => {
res.write("it is /")
res.end()
})
myHttp.get("/aa/bb",(req,res) => {
res.write("it is /aa/bb")
res.end()
})
myHttp.listen(3003)
複製代碼
是否是有幾分自定義模塊的味道了?