重写 Express API
Express API 由请求和响应对象上的各种方法和属性组成。它们通过原型继承。Express API 有两个扩展点:
express.request
和express.response
处的全局原型。app.request
和app.response
处的应用特定原型。
更改全局原型将影响同一进程中所有加载的 Express 应用。如果需要,可以通过在创建新应用后仅更改应用特定原型,使更改仅限于特定应用。
方法
您可以通过分配自定义函数,用自己的实现覆盖现有方法的签名和行为。
以下是覆盖 res.sendStatus 行为的示例。
app.response.sendStatus = function (statusCode, type, message) {
// code is intentionally kept simple for demonstration purpose
return this.contentType(type)
.status(statusCode)
.send(message)
}
上述实现完全改变了 res.sendStatus
的原始签名。它现在接受状态码、编码类型以及要发送给客户端的消息。
现在可以这样使用被覆盖的方法:
res.sendStatus(404, 'application/json', '{"error":"resource not found"}')
属性
Express API 中的属性要么是:
- 赋值属性(例如:
req.baseUrl
,req.originalUrl
) - 定义为 getter(例如:
req.secure
,req.ip
)
由于第 1 类属性是在当前请求-响应周期的上下文中动态分配到 request
和 response
对象上的,因此它们的行为无法被覆盖。
第 2 类属性可以使用 Express API 扩展 API 进行覆盖。
以下代码重写了 req.ip
值的获取方式。现在,它只返回 Client-IP
请求头的值。
Object.defineProperty(app.request, 'ip', {
configurable: true,
enumerable: true,
get () { return this.get('Client-IP') }
})
原型
为了提供 Express API,传递给 Express 的请求/响应对象(例如通过 app(req, res)
)需要继承自相同的原型链。默认情况下,请求是 http.IncomingRequest.prototype
,响应是 http.ServerResponse.prototype
。
除非必要,建议仅在应用级别进行此操作,而非全局。同时,请注意所使用的原型应尽可能与默认原型在功能上保持一致。
// Use FakeRequest and FakeResponse in place of http.IncomingRequest and http.ServerResponse
// for the given app reference
Object.setPrototypeOf(Object.getPrototypeOf(app.request), FakeRequest.prototype)
Object.setPrototypeOf(Object.getPrototypeOf(app.response), FakeResponse.prototype)