Context
HTTP 上下文( context) 是一个比较特殊的对象。 context 对象持有 request body, cookies, headers 当前登录的 logged in user,以及给定 HTTP 的更多请求信息。
HTTP context 采用引用传递的方式,传递给路由(route)、中间件(middleware),HTTP hooks 和异常处理程序。
Route.get('/', ({ request, auth, response }) => {
/**
* 请求 URL
*/
console.log(request.url())
/**
* 请求body + 参数
*/
console.log(request.all())
/**
* 响应
*/
response.send('hello world')
response.send({ hello: 'world' })
/**
* 当配置身份验证时,可用
*/
console.log(auth.user)
})
确保在控制器方法(controller method)中,明确定义 context 的类型。
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
class HomeController {
public async index({ request, response }: HttpContextContract) {
}
}
是否与 Express 中的 req
和 res
对象有某些关联?#
你不会在 AdonisJS 中看到任何 req
或 res
对象,这是因为所有的一切(包括请求和响应)都是 HTTP 上下文(context)的一部分。
此外,我们鼓励你将自定义属性添加到 ctx
对象,而不是 request
对象。请参阅 扩展上下文。
访问 HTTP 上下文#
AdonisJS 使用 Node.js AsyncLocalStorage 使 HTTP 上下文在程序内的任何位置都可以访问到.
你可以按如下方式访问当前请求的上下文:
注意:在使用
HttpContext.get
方法之前,请务必仔细阅读 Async Local Storage 指南。
import HttpContext from '@ioc:Adonis/Core/HttpContext'
class SomeService {
public async someOperation() {
const ctx = HttpContext.get()
}
}
属性#
以下是 HTTP
上下文中可用的属性列表,当你将安装新包时,它们也可能会向该对象添加更多属性。
请求#
参考 HTTP 请求
Route.get('/', async ({ request }) => {})
响应#
参考 HTTP 响应
Route.get('/', async ({ response }) => {})
日志#
对日志记录器实例的引用。为每个 HTTP
请求创建一个具有唯一 request ID 的 child logger 实例。
Route.get('/', async ({ logger }) => {})
路由#
对当前 HTTP
请求匹配路由,路由具有以下属性。
pattern
: 路由模式handler
: 路由处理器middleware
: 路由中间件数组name
: 路由名称 (如果有)
Route.get('/', async ({ route }) => {})
参数#
路由参数对象。
Route.get('users/:id', async ({ params }) => {
console.log(params.id)
})
路由子域#
路由子域的对象,仅当路由在域中注册时可用。
Route.group(() => {
Route.get('/', async ({ subdomains }) => {
console.log(subdomains.tenant)
})
}).domain(':tenant.adonisjs.com')
会话#
参考 Session 对象,仅在安装 @adonisjs/session
包时可用。
Route.get('/', async ({ session }) => {
session.get('cart_value')
})
认证#
参考 Auth 对象,仅在安装 @adonisjs/auth
包时可用。
Route.get('/', async ({ auth }) => {
console.log(auth.user)
})
视图#
参考 视图对象,仅在安装 @adonisjs/view
包时可用。
Route.get('/', async ({ view }) => {
return view.render('welcome')
})
合作#
参考 合作对象,仅在安装 @adonisjs/ally
软件包时可用。
Route.get('/', async ({ ally }) => {
return ally.use('github').redirect()
})
安检#
参考 Bouncer 对象,仅在安装 @adonisjs/bouncer
包时可用。
Route.get('/', async ({ bouncer }) => {
await bouncer.authorize('viewPost', post)
})
i18n#
参考 I18n 对象,仅在安装 @adonisjs/i18n
软件包时可用。
Route.get('/', async ({ i18n }) => {
await i18n.formatCurrency(100, { currency: 'EUR' })
})
扩展上下文#
HTTP 上下文对象可由其他包或你自己的应用程序代码扩展,一个常见的用法是在中间件中添加自定义属性,例如:
import geoip from 'geoip-lite'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class UserLocationMiddleware {
public async handle(ctx: HttpContextContract, next: () => Promise<void>) {
ctx.location = geoip.lookup(ctx.request.ip())
await next()
}
}
在这里,我们向 ctx
添加了一个自定义 location
属性,你可以在路由处理程序或即将推出的中间件中访问该属性。
通知 TypeScript 自定义属性#
location
属性是在运行时添加的;所以 TypeScript 不知道它。为了告知 TypeScript 该属性的存在,我们将使用 declaration merging 并将属性添加到 HttpContextContract
接口。
在路径 contracts/context.ts
处创建一个新文件 (文件名不重要) 并在其中粘贴以下内容:
// 文件名: contracts/context.ts
declare module '@ioc:Adonis/Core/HttpContext' {
import { Lookup } from 'geoip-lite'
interface HttpContextContract {
location: Lookup | null
}
}
这样,TypeScript 就不会抱怨 ctx
对象上缺少的属性。
使用 getter 和宏#
你还可以使用 getter 和宏将自定义属性添加到 ctx
对象。在前面的示例中,我们向 ctx
对象添加了一个 实例属性。但是,getter 和宏会在 类的原型 上添加属性。
此外,这次不需要创建中间件,因为你只需定义一次宏 /getter,就可用于 HttpContext 类的所有实例。
打开预设的 providers/AppProvider.ts
文件并将以下代码粘贴到 boot
方法中:
// 文件名: providers/AppProvider.ts
import geoip from 'geoip-lite'
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
export default class AppProvider {
public static needsApplication = true
constructor(protected app: ApplicationContract) {}
public async boot() {
const HttpContext = this.app.container.use('Adonis/Core/HttpContext')
HttpContext.getter('location', function location() {
return geoip.lookup(this.request.ip())
})
}
}
默认情况下,每次访问都会评估 getter。但是,你也可以将它们标记为单例,如下所示:
HttpContext.getter(
'location',
function location() {
return geoip.lookup(this.request.ip())
},
true // 👈 注册为单例
)
宏#
Getter 只能作为属性访问,但是,宏既可以是属性,也可以是方法。
HttpContext.macro('getLocation', function location() {
return geoip.lookup(this.request.ip())
})
// 调用方式
ctx.getLocation()
或添加一个字面值。
HttpContext.macro('pid', process.pid)
// 调用方式
ctx.pid
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。