HTTP 上下文
每个 HTTP 请求都会生成 HTTP Context 类 的一个新实例,并将其传递给路由 handler、中间件和异常处理器。
HTTP 上下文持有你可能需要的所有与 HTTP 请求相关的信息。例如:
- 你可以使用 ctx.request 属性访问请求体、请求头和查询参数。
- 你可以使用 ctx.response 属性来响应 HTTP 请求。
- 使用 ctx.auth 属性访问已登录的用户。
- 使用 ctx.bouncer 属性对用户操作进行授权。
- 等等。
简而言之,上下文是一个请求专属的存储,持有当前请求的所有信息。
获取 HTTP 上下文
HTTP 上下文是通过引用传递给路由 handler、中间件和异常处理器的,你可以按如下方式访问它。
路由 handler
router handler 接收 HTTP 上下文作为第一个参数。
import router from '@adonisjs/core/services/router'
router.get('/', (ctx) => {
console.log(ctx.inspect())
})
import router from '@adonisjs/core/services/router'
router.get('/', ({ request, response }) => {
console.log(request.url())
console.log(request.headers())
console.log(request.qs())
console.log(request.body())
response.send('hello world')
response.send({ hello: 'world' })
})
控制器方法
控制器方法(类似于 router handler)接收 HTTP 上下文作为第一个参数。
import { HttpContext } from '@adonisjs/core/http'
export default class HomeController {
async index({ request, response }: HttpContext) {
}
}
中间件类
中间件类 的 handle 方法接收 HTTP 上下文作为第一个参数。
import { HttpContext } from '@adonisjs/core/http'
export default class AuthMiddleware {
async handle({ request, response }: HttpContext) {
}
}
异常处理器类
全局异常处理器 类的 handle 和 report 方法接收 HTTP 上下文作为第二个参数。第一个参数是 error 属性。
import {
HttpContext,
HttpExceptionHandler
} from '@adonisjs/core/http'
export default class ExceptionHandler extends HttpExceptionHandler {
async handle(error: unknown, ctx: HttpContext) {
return super.handle(error, ctx)
}
async report(error: unknown, ctx: HttpContext) {
return super.report(error, ctx)
}
}
使用依赖注入注入 HTTP 上下文
如果你在整个应用中都使用依赖注入,你可以通过类型提示 HttpContext 类来将 HTTP 上下文注入到一个类或方法中。
确保 #middleware/container_bindings_middleware 中间件已在 start/kernel.ts 文件中注册。该中间件是从容器中解析请求专属的值(即 HttpContext 类)所必需的。
另请参阅:IoC 容器指南
import { inject } from '@adonisjs/core'
import { HttpContext } from '@adonisjs/core/http'
@inject()
export default class UserService {
constructor(protected ctx: HttpContext) {}
all() {
// method implementation
}
}
为了让自动依赖解析生效,你必须在控制器中注入 UserService。请记住,控制器方法的第一个参数始终是上下文,其余参数则通过 IoC 容器注入。
import { inject } from '@adonisjs/core'
import { HttpContext } from '@adonisjs/core/http'
import UserService from '#services/user_service'
export default class UsersController {
@inject()
index(ctx: HttpContext, userService: UserService) {
return userService.all()
}
}
仅此而已!UserService 现在会自动接收到当前 HTTP 请求的一个实例。对于嵌套的依赖,你也可以重复同样的过程。
从应用中的任何位置访问 HTTP 上下文
依赖注入是接受 HTTP 上下文作为类构造函数或方法依赖的一种方式,然后依靠容器来为你解析它。
然而,重构应用并在任何地方都使用依赖注入并非硬性要求。你也可以使用 Node.js 提供的 Async local storage 从应用中的任何位置访问 HTTP 上下文。
我们有一份专门的指南介绍 Async local storage 的工作原理,以及 AdonisJS 如何使用它来提供对 HTTP 上下文的全局访问。
在下面的示例中,UserService 类使用 HttpContext.getOrFail 方法来获取当前请求的 HTTP 上下文实例。
import { HttpContext } from '@adonisjs/core/http'
export default class UserService {
all() {
const ctx = HttpContext.getOrFail()
console.log(ctx.request.url())
}
}
下面的代码块展示了 UserService 类在 UsersController 中的用法。
import { HttpContext } from '@adonisjs/core/http'
import UserService from '#services/user_service'
export default class UsersController {
index(ctx: HttpContext) {
const userService = new UserService()
return userService.all()
}
}
HTTP 上下文属性
以下是通过 HTTP 上下文可以访问的属性列表。随着你安装新的软件包,它们可能会向上下文中添加额外的属性。
-
ctx.request
-
对 HTTP Request 类 实例的引用。
-
ctx.response
-
对 HTTP Response 类 实例的引用。
-
ctx.logger
-
对为给定 HTTP 请求创建的 logger 实例的引用。
-
ctx.route
-
当前 HTTP 请求所匹配的路由。
route属性是一个 StoreRouteNode 类型的对象 -
ctx.params
-
路由参数的对象
-
ctx.subdomains
-
路由子域名的对象。仅当路由属于某个动态子域名时才存在
-
ctx.session
-
对为当前 HTTP 请求创建的 Session 实例的引用。
-
ctx.auth
-
对 Authenticator 类 实例的引用。了解更多关于身份认证的内容。
-
ctx.view
-
对 Edge 渲染器实例的引用。在 视图与模板指南 中了解更多关于 Edge 的内容
-
ctx.ally
-
对 Ally Manager 类 实例的引用,用于在应用中实现社交登录。了解更多关于 Ally 的内容
-
ctx.bouncer
-
ctx.i18n
扩展 HTTP 上下文
你可以使用宏或 getter 向 HTTP 上下文类添加自定义属性。如果你还不熟悉宏的概念,请务必先阅读扩展 AdonisJS 指南。
import { HttpContext } from '@adonisjs/core/http'
HttpContext.macro('aMethod', function (this: HttpContext) {
return value
})
HttpContext.getter('aProperty', function (this: HttpContext) {
return value
})
由于宏和 getter 是在运行时添加的,你必须通过模块扩充(module augmentation)来告知 TypeScript 它们的类型。
import { HttpContext } from '@adonisjs/core/http'
declare module '@adonisjs/core/http' {
export interface HttpContext {
aMethod: () => ValueType
aProperty: ValueType
}
}
HttpContext.macro('aMethod', function (this: HttpContext) {
return value
})
HttpContext.getter('aProperty', function (this: HttpContext) {
return value
})
在测试中创建虚拟上下文
你可以使用 testUtils 服务在测试中创建一个虚拟的 HTTP 上下文。
该上下文实例没有绑定到任何路由;因此,ctx.route 和 ctx.params 的值会是 undefined。不过,如果被测代码需要,你可以手动为这些属性赋值。
import testUtils from '@adonisjs/core/services/test_utils'
const ctx = testUtils.createHttpContext()
默认情况下,createHttpContext 方法对 req 和 res 对象使用假数据。不过,你可以像下面的示例所示为这些属性定义自定义值。
import { createServer } from 'node:http'
import testUtils from '@adonisjs/core/services/test_utils'
createServer((req, res) => {
const ctx = testUtils.createHttpContext({
req,
res
})
})
使用 HttpContext 工厂
testUtils 服务仅在 AdonisJS 应用内部可用;因此,如果你正在构建一个软件包并且需要访问一个假的 HTTP 上下文,你可以使用 HttpContextFactory 类。
import { HttpContextFactory } from '@adonisjs/core/factories/http'
const ctx = new HttpContextFactory().create()