|
| 1 | +--- |
| 2 | +title: express |
| 3 | +author: 高红翔 |
| 4 | +date: 2024-10-08 14:48:34 |
| 5 | +categories: |
| 6 | +tags: |
| 7 | +--- |
| 8 | + |
| 9 | +### 基本使用 |
| 10 | + |
| 11 | +```js |
| 12 | +import express from "express" |
| 13 | +const app = express() |
| 14 | +app.use( |
| 15 | + function (req, res, next) { |
| 16 | + console.log("middlware 1") |
| 17 | + next() |
| 18 | + }, |
| 19 | + function (req, res, next) { |
| 20 | + console.log("middlware 2") |
| 21 | + next("a") |
| 22 | + } |
| 23 | +) |
| 24 | +app.use(function (req, res, next) { |
| 25 | + console.log("middlware 3") |
| 26 | + next() |
| 27 | +}) |
| 28 | +app.get("/user", function (req, res, next) { |
| 29 | + // console.log(1, req.a) |
| 30 | + console.log("222=>", 222) |
| 31 | + next() |
| 32 | +}) |
| 33 | +app.use((err, req, res, next) => { |
| 34 | + res.end(err) |
| 35 | +}) |
| 36 | +app.listen(3001) |
| 37 | +``` |
| 38 | + |
| 39 | +### 核心实现 |
| 40 | + |
| 41 | +#### application.js |
| 42 | + |
| 43 | +> application.js 是框架的主入口,负责启动 HTTP 服务器并处理请求。 |
| 44 | +
|
| 45 | +- **懒加载路由系统**:`lazy_route` 方法确保在需要时才加载路由系统。 |
| 46 | + |
| 47 | +- **use 方法**:用于注册全局中间件。 |
| 48 | + |
| 49 | +- **listen 方法**:创建 HTTP 服务器,通过调用 `this.router.handle` 处理每个请求。如果没有匹配的路由,则调用 `done` 方法结束请求,返回错误信息。 |
| 50 | + |
| 51 | +```js |
| 52 | +import http from "http" |
| 53 | +import methods from "methods" |
| 54 | +import Router from "./router/index.js" |
| 55 | + |
| 56 | +function Application() {} |
| 57 | + |
| 58 | +Application.prototype.lazy_route = function () { |
| 59 | + if (!this.router) { |
| 60 | + this.router = new Router() // 将路由系统进行了懒加载处理(一般都是要加载的) |
| 61 | + } |
| 62 | +} |
| 63 | +Application.prototype.use = function () { |
| 64 | + this.lazy_route() |
| 65 | + this.router.use(...arguments) // 交给路由系统来处理 |
| 66 | +} |
| 67 | +Application.prototype.listen = function (...args) { |
| 68 | + const server = http.createServer((req, res) => { |
| 69 | + function done() { |
| 70 | + // 如果路由系统中的层不存在则调用此方法来结束响应 |
| 71 | + res.end(`Cannot ${req.method} ${req.url}`) |
| 72 | + } |
| 73 | + // 交给路由系统来做匹配,如果匹配不到就调用done |
| 74 | + this.router.handle(req, res, done) |
| 75 | + }) |
| 76 | + server.listen(...args) |
| 77 | +} |
| 78 | +methods.forEach((method) => { |
| 79 | + Application.prototype[method] = function (path, ...handlers) { |
| 80 | + // 让路由系统处理逻辑 |
| 81 | + this.lazy_route() |
| 82 | + this.router[method](path, handlers) |
| 83 | + } |
| 84 | +}) |
| 85 | + |
| 86 | +export default Application |
| 87 | +``` |
| 88 | + |
| 89 | +#### Router.js |
| 90 | + |
| 91 | +> router.js 是路由系统的核心,管理路由栈(stack)并实现路由匹配逻辑。 |
| 92 | +
|
| 93 | +- **路由注册**:为每个 HTTP 方法创建相应的路由,使用 `Layer` 类管理路径,并通过 `dispatch` 方法执行相应的路由处理函数。 |
| 94 | +- **中间件注册**:`use` 方法用于注册全局或特定路径的中间件。 |
| 95 | +- **请求处理**:`handle` 方法用于匹配请求路径和方法,逐层执行中间件和路由处理函数。如果存在错误,则调用错误处理中间件。 |
| 96 | + |
| 97 | +```js |
| 98 | +import url from "url" |
| 99 | +import methods from "methods" |
| 100 | +import Layer from "./Layer.js" |
| 101 | +import Route from "./route.js" |
| 102 | +function Router() { |
| 103 | + this.stack = [] |
| 104 | +} |
| 105 | +methods.forEach((method) => { |
| 106 | + Router.prototype[method] = function (path, handlers) { |
| 107 | + // 调用类来管理路径 |
| 108 | + let route = new Route() |
| 109 | + handlers.forEach((handler) => { |
| 110 | + route[method](handler) |
| 111 | + }) |
| 112 | + let layer = new Layer(path, route.dispatch.bind(route)) |
| 113 | + // 每个路由的层都有一个route属性,对应存放自己的真实路基的 |
| 114 | + layer.route = route |
| 115 | + this.stack.push(layer) |
| 116 | + } |
| 117 | +}) |
| 118 | +Router.prototype.use = function (path, ...handlers) { |
| 119 | + if (typeof path === "function") { |
| 120 | + handlers = [path, ...handlers] // path就是处理函数 |
| 121 | + path = "/" // 如果没写路径就是 / 匹配所有的路径 |
| 122 | + } |
| 123 | + handlers.forEach((handler) => { |
| 124 | + const layer = new Layer(path, handler) |
| 125 | + layer.route = undefined // 中间件没有路由这个对象 |
| 126 | + this.stack.push(layer) |
| 127 | + }) |
| 128 | +} |
| 129 | +Router.prototype.handle = function (req, res, out) { |
| 130 | + const { pathname, query } = url.parse(req.url, true) |
| 131 | + const method = req.method.toLowerCase() |
| 132 | + let idx = 0 |
| 133 | + let next = (err) => { |
| 134 | + if (this.stack.length == idx) return out() |
| 135 | + let layer = this.stack[idx++] // 拿出第一个层 |
| 136 | + if (err) { |
| 137 | + if (!layer.route) { |
| 138 | + // 有错误找中间件,而且 要找参数是4个的中间件 |
| 139 | + if (layer.handler.length === 4) { |
| 140 | + layer.handler(err, req, res, next) |
| 141 | + } else { |
| 142 | + next(err) // 普通中间件继续带着错误向下走 |
| 143 | + } |
| 144 | + } else { |
| 145 | + // 有错误但是是路由,要带着 错误继续往下走 |
| 146 | + next(err) |
| 147 | + } |
| 148 | + } else { |
| 149 | + // 因为错误处理中间件定义在了 router.stack中 ,如果有err就去这个stack中查找错误处理中间件 |
| 150 | + if (layer.match(pathname)) { |
| 151 | + if (layer.route) { |
| 152 | + // 路由 |
| 153 | + if (layer.route.methods[req.method.toLowerCase()]) { |
| 154 | + // 需要匹配方法 |
| 155 | + layer.handle_request(req, res, next) // route.dispatch |
| 156 | + } else { |
| 157 | + next() // 方法不一致直接向下走 |
| 158 | + } |
| 159 | + } else { |
| 160 | + // 中间件无需匹配方法, 没有错误不能执行错误处理中间件 |
| 161 | + if (layer.handler.length !== 4) { |
| 162 | + layer.handle_request(req, res, next) // route.dispatch |
| 163 | + } else { |
| 164 | + next() |
| 165 | + } |
| 166 | + } |
| 167 | + } else { |
| 168 | + next() |
| 169 | + } |
| 170 | + } |
| 171 | + } |
| 172 | + |
| 173 | + next() // 默认在路由中筛查 |
| 174 | +} |
| 175 | + |
| 176 | +export default Router |
| 177 | +``` |
| 178 | + |
| 179 | +#### Layer.js |
| 180 | + |
| 181 | +> layer.js 定义了路由和中间件的抽象层。每个路由或中间件对应一个 Layer 实例。 |
| 182 | +
|
| 183 | +- **匹配路径**:`match` 方法用于检查当前 `Layer` 是否匹配请求路径。 |
| 184 | + |
| 185 | +- **请求处理**:`handle_request` 方法调用具体的处理函数。 |
| 186 | + |
| 187 | +```js |
| 188 | +// 路由中对应的层 |
| 189 | +function Layer(path, handler) { |
| 190 | + this.path = path |
| 191 | + this.handler = handler |
| 192 | +} |
| 193 | +Layer.prototype.match = function (path) { |
| 194 | + if (this.path === path) { |
| 195 | + return true |
| 196 | + } |
| 197 | + if (!this.route) { |
| 198 | + // 中间件 |
| 199 | + if (this.path === "/") { |
| 200 | + // 中间件是/都能匹配 |
| 201 | + return true |
| 202 | + } |
| 203 | + return path.startsWith(this.path + "/") |
| 204 | + } |
| 205 | + return false |
| 206 | +} |
| 207 | + |
| 208 | +Layer.prototype.handle_request = function (req, res, next) { |
| 209 | + return this.handler(req, res, next) |
| 210 | +} |
| 211 | +export default Layer |
| 212 | +``` |
| 213 | + |
| 214 | +#### Route.js |
| 215 | + |
| 216 | +> route.js 负责管理每个特定路由的处理函数,并调度(dispatch)处理。 |
| 217 | +
|
| 218 | +- **方法注册**:为每个路由绑定特定 HTTP 方法(如 `get`、`post`),并将处理函数存储在 `stack` 中。 |
| 219 | + |
| 220 | +- **调度处理**:`dispatch` 方法根据请求方法依次执行对应的处理函数。 |
| 221 | + |
| 222 | +```js |
| 223 | +import methods from "methods" |
| 224 | +import Layer from "./Layer.js" |
| 225 | +function Route() { |
| 226 | + this.stack = [] |
| 227 | + this.methods = {} // 用来描述这个route中有什么方法 |
| 228 | +} |
| 229 | +methods.forEach((method) => { |
| 230 | + Route.prototype[method] = function (handler) { |
| 231 | + let layer = new Layer("里层的用户的逻辑不需要这个path", handler) |
| 232 | + layer.method = method |
| 233 | + this.methods[method] = true // {get:true} |
| 234 | + this.stack.push(layer) |
| 235 | + } |
| 236 | +}) |
| 237 | + |
| 238 | +Route.prototype.dispatch = function (req, res, out) { |
| 239 | + let idx = 0 |
| 240 | + const next = (err) => { |
| 241 | + console.log("run") |
| 242 | + if (err) return out(err) |
| 243 | + if (idx === this.stack.length) return out() |
| 244 | + let layer = this.stack[idx++] |
| 245 | + if (req.method.toLowerCase() === layer.method) { |
| 246 | + // 用户绑定的方法 |
| 247 | + layer.handle_request(req, res, next) |
| 248 | + } else { |
| 249 | + next() |
| 250 | + } |
| 251 | + } |
| 252 | + next() |
| 253 | +} |
| 254 | +export default Route |
| 255 | +``` |
0 commit comments