咱们知道最原生的处理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)
复制代码
是否是有几分自定义模块的味道了?