使用http模块
实现步骤
- 创建
SimpleExpress构造函数,实现use、get、post、listen等方法,存储传入的路由和中间件
class SimpleExpress {
constructor(){
this.routes = {
all: [],
get: [],
post: []
}
}
use(){}
get(){}
post(){}
listen(){}
}
- 收集并注册中间件
register
class SimpleExpress {
/* ... */
use(...args){
this.routes.all.push(this.register(...args))
}
get(...args){
this.routes.get.push(this.register(...args))
}
post(...args){
this.routes.post.push(this.register(...args))
}
// 返回收集的路由和中间件
register(...args){
const path = args[0] // 第一个参数判断
const info = {}
if(typeof path === 'string') {
info.path = path
info.stack = Array.prototype.slice.call(args, 1) // 截取第二位及以后
}else {
info.path = '/'
info.stack = Array.prototype.slice.call(args, 0)
}
return info
}
}
- 通过 path 匹配所需中间件
match
class SimpleExpress {
/* ... */
match(method, url){
if(url !== '/favicon.ico') { // 过滤ico图标
return []
}
let curRoutes = []
curRoutes = curRoutes.concat(this.routes.all) // 公共中间件
curRoutes = curRoutes.concat(this.routes[method]) // 通用方法中间件
let stack = []
curRoutes.forEach( (routeInfo) => {
if(url.indexOf(routeInfo.path) === 0 ){
// 路径匹配
stack = stack.concat(routeInfo.stack)
}
})
return stack
}
}
- 执行匹配的中间件,实现
handle即核心next()函数
class SimpleExpress {
/* ... */
callback(){
return (req, res, next) => {
res.json = (data) => {
res.setHeader('Content-type', 'application/json')
res.end(JSON.stringify(data))
}
const url = req.url
const method = req.method.toLowerCase()
const resultRoutes = this.match(method, url)
this.handle(req, res, resultRoutes)
}
}
// 实现 next() 核心
handle(req, res, stack){
const next = () => {
const firstRoute = resultRoutes.shift()
if(firstRoute){
handle(req, res, next)
}
}
next()
}
}
- 实现
listen创建服务 监听端口
const http = require("http")
class SimpleExpress {
/* ... */
listen(...args){
const app = http.createServer(this.callback())
server.listen(...args)
}
}
全部代码
const http = require('http')
const Slice = Array.prototype.slice
class SimpleExpress {
constructor(){
this.routes = {
all: [],
get: [],
post: []
}
}
register(path){
const info = {}
// 判断是否又路径
if(typeof path === 'string'){
info.path = path
info.stack = Slice.call(arguments, 1) // 取到路由
} else {
info.path = '/'
info.stack = Slice.call(arguments, 0) // 取到路由
}
return info
}
use(){
this.routes.push(this.register.apply(this.atguments))
}
get(){
this.routes.push(this.register.apply(this.atguments))
}
post(){
this.routes.push(this.register.apply(this.atguments))
}
listen(...args){
const server = http.createServer(this.callback())
server.listen(...args)
}
// 传入
callback(){
return (req, res) => {
res.json(
res.setHeader('Content-type', 'application/json')
res.end(JSON.stringify(data))
)
const url = req.url
const method = req.method.toLowerCase()
// 根据 url、method获取匹配的中间件
const routeList = this.match(method, url)
// 执行中间件
this.handler(req, res, routeList)
}
}
// 匹配中间件
match(method, url){
if(url.indexOf('/favicon.ico') === 0){ return [] }
const currentRoutes = [].concat(this.routes.all, this.routes[method])
let stack = []
currentRoutes.forEach( (route) => {
if(url.indexOf(route.path) === 0){
stack = stack.concat(route.stack)
}
})
return stack
}
// 执行中间件 实现 next()
handler(req, res, routes){
const next = (req, res) => {
const middleware = routes.shift()
if(middleware) { middleware(req, res, next) }
}
next()
}
}
总结
-
收集所有使用的中间件
-
通过
method和url匹配 path 路径 -
核心: 实现
next()执行所有匹配的中间件