Request
request 类 的实例持有正在进行的 HTTP 请求的数据,包括请求体、对上传文件的引用、cookie、请求头等等。可以使用 ctx.request 属性访问请求实例。
查询字符串与路由参数
request.qs 方法将解析后的查询字符串作为对象返回。
import router from '@adonisjs/core/services/router'
router.get('posts', async ({ request }) => {
/*
* URL: /?sort_by=id&direction=desc
* qs: { sort_by: 'id', direction: 'desc' }
*/
request.qs()
})
request.params 方法返回一个 路由参数 的对象。
import router from '@adonisjs/core/services/router'
router.get('posts/:slug/comments/:id', async ({ request }) => {
/*
* URL: /posts/hello-world/comments/2
* params: { slug: 'hello-world', id: '2' }
*/
request.params()
})
你可以使用 request.param 方法访问单个参数。
import router from '@adonisjs/core/services/router'
router.get('posts/:slug/comments/:id', async ({ request }) => {
const slug = request.param('slug')
const commentId = request.param('id')
})
请求体
AdonisJS 使用注册在 start/kernel.ts 文件中的 body-parser 中间件 来解析请求体。
你可以使用 request.body() 方法访问请求体。该方法将解析后的请求体作为对象返回。
import router from '@adonisjs/core/services/router'
router.post('/', async ({ request }) => {
console.log(request.body())
})
request.all 方法返回请求体和查询字符串的合并副本。
import router from '@adonisjs/core/services/router'
router.post('/', async ({ request }) => {
console.log(request.all())
})
挑选特定值
request.input、request.only 和 request.except 方法可以从请求数据中挑选特定属性。所有挑选方法都会在请求体和查询字符串中查找值。
request.only 方法返回的对象的只包含所提到的属性。
import router from '@adonisjs/core/services/router'
router.post('login', async ({ request }) => {
const credentials = request.only(['email', 'password'])
console.log(credentials)
})
request.except 方法返回排除了所提到属性的对象。
import router from '@adonisjs/core/services/router'
router.post('register', async ({ request }) => {
const userDetails = request.except(['password_confirmation'])
console.log(userDetails)
})
request.input 方法返回特定属性的值。可选地,你可以将默认值作为第二个参数传入。当实际值缺失时,会返回默认值。
import router from '@adonisjs/core/services/router'
router.post('comments', async ({ request }) => {
const email = request.input('email')
const commentBody = request.input('body')
})
类型安全的请求体
默认情况下,AdonisJS 不会对 request.all、request.body 或挑选方法强制数据类型,因为它无法提前知道请求体预期的内容。
为了确保类型安全,你可以使用 校验器 来校验和解析请求体,确保所有值都正确且符合预期的类型。
请求 URL
request.url 方法返回相对于主机名的请求 URL。默认情况下,返回值不包含查询字符串。不过,你可以通过调用 request.url(true) 获取带查询字符串的 URL。
import router from '@adonisjs/core/services/router'
router.get('/users', async ({ request }) => {
/*
* URL: /users?page=1&limit=20
* url: /users
*/
request.url()
/*
* URL: /users?page=1&limit=20
* url: /users?page=1&limit=20
*/
request.url(true) // 返回查询字符串
})
request.completeUrl 方法返回完整的 URL,包含主机名。同样,除非显式指定,返回值不包含查询字符串。
import router from '@adonisjs/core/services/router'
router.get('/users', async ({ request }) => {
request.completeUrl()
request.completeUrl(true) // 返回查询字符串
})
请求头
request.headers 方法将请求头作为对象返回。
import router from '@adonisjs/core/services/router'
router.get('/', async ({ request }) => {
console.log(request.headers())
})
你可以使用 request.header 方法访问单个头的值。
import router from '@adonisjs/core/services/router'
router.get('/', async ({ request }) => {
request.header('x-request-id')
// 头名称不区分大小写
request.header('X-REQUEST-ID')
})
请求方法
request.method 方法返回当前请求的 HTTP 方法。当启用了表单方法欺骗 时,此方法返回被欺骗的方法;你可以使用 request.intended 方法获取原始的请求方法。
import router from '@adonisjs/core/services/router'
router.patch('posts', async ({ request }) => {
/**
* 用于路由匹配的方法
*/
console.log(request.method())
/**
* 实际的请求方法
*/
console.log(request.intended())
})
用户 IP 地址
request.ip 方法返回当前 HTTP 请求的用户 IP 地址。此方法依赖于由 Nginx 或 Caddy 等代理服务器设置的 X-Forwarded-For 头。
请阅读 配置受信任代理 一节,以配置你的应用应当信任的代理。
import router from '@adonisjs/core/services/router'
router.get('/', async ({ request }) => {
console.log(request.ip())
})
request.ips 方法返回由中间代理设置的所有 IP 地址的数组。该数组按从最可信到最不可信的 IP 地址排序。
import router from '@adonisjs/core/services/router'
router.get('/', async ({ request }) => {
console.log(request.ips())
})
定义自定义 getIp 方法
如果受信任的代理设置不足以确定正确的 IP 地址,你可以实现自定义的 getIp 方法。
该方法定义在 config/app.ts 文件的 http 设置对象内。
export const http = defineConfig({
getIp(request) {
const ip = request.header('X-Real-Ip')
if (ip) {
return ip
}
return request.ips()[0]
}
})
内容协商
AdonisJS 提供了多个用于内容协商 的方法,通过解析所有常用支持的 Accept 头来实现。例如,你可以使用 request.types 方法获取给定请求接受的所有内容类型列表。
request.types 方法的返回值按客户端的偏好排序(最优先的在前)。
import router from '@adonisjs/core/services/router'
router.get('/', async ({ request }) => {
console.log(request.types())
})
以下是内容协商方法的完整列表。
| Method | 使用的 HTTP 头 |
|---|---|
| types | Accept |
| languages | Accept-language |
| encodings | Accept-encoding |
| charsets | Accept-charset |
有时你希望根据服务器能够支持的内容来查找偏好的内容类型。
为此,你可以使用 request.accepts 方法。该方法接受一个受支持的内容类型数组,并在检查 Accept 头后返回最偏好的一个。当无法找到匹配项时,返回 null 值。
import router from '@adonisjs/core/services/router'
router.get('posts', async ({ request, view }) => {
const posts = [
{
title: 'Adonis 101',
},
]
const bestMatch = request.accepts(['html', 'json'])
switch (bestMatch) {
case 'html':
return view.render('posts/index', { posts })
case 'json':
return posts
default:
return view.render('posts/index', { posts })
}
})
与 request.accept 类似,以下方法可用于查找其他 Accept 头的偏好值。
// 偏好语言
const language = request.language(['fr', 'de'])
// 偏好编码
const encoding = request.encoding(['gzip', 'br'])
// 偏好字符集
const charset = request.charset(['utf-8', 'hex', 'ascii'])
生成请求 ID
请求 ID 通过为每个 HTTP 请求分配一个唯一 ID,帮助你从日志中调试和追踪应用问题。默认情况下,请求 ID 的创建是关闭的。不过,你可以在 config/app.ts 文件中启用它。
请求 ID 是使用 cuid2 包生成的。在生成 ID 之前,我们会检查 X-Request-Id 请求头,并使用其值(如果存在)。
export const http = defineConfig({
generateRequestId: true
})
启用后,你可以使用 request.id 方法访问该 ID。
router.get('/', ({ request }) => {
// ckk9oliws0000qt3x9vr5dkx7
console.log(request.id())
})
同一个请求 ID 也会被添加到使用 ctx.logger 实例生成的所有日志中。
router.get('/', ({ logger }) => {
// { msg: 'hello world', request_id: 'ckk9oliws0000qt3x9vr5dkx7' }
logger.info('hello world')
})
配置受信任代理
大多数 Node.js 应用部署在 Nginx 或 Caddy 等代理服务器后面。因此我们必须依赖 X-Forwarded-Host、X-Forwarded-For 和 X-Forwarded-Proto 等 HTTP 头来了解发出 HTTP 请求的真实终端客户端。
这些头只有在你的 AdonisJS 应用能够信任来源 IP 地址时才会被使用。
你可以在 config/app.ts 文件中使用 http.trustProxy 配置选项来配置要信任哪些 IP 地址。
import proxyAddr from 'proxy-addr'
export const http = defineConfig({
trustProxy: proxyAddr.compile(['127.0.0.1/8', '::1/128'])
})
trustProxy 的值也可以是一个函数。如果 IP 地址受信任,该方法应返回 true;否则返回 false。
export const http = defineConfig({
trustProxy: (address) => {
return address === '127.0.0.1' || address === '123.123.123.123'
}
})
如果你在同一台服务器上与应用代码一起运行 Nginx,你需要信任回环 IP 地址,即 127.0.0.1。
import proxyAddr from 'proxy-addr'
export const http = defineConfig({
trustProxy: proxyAddr.compile('loopback')
})
假设你的应用只能通过负载均衡器访问,而你又没有该负载均衡器的 IP 地址列表。那么,你可以通过定义一个总是返回 true 的回调来信任该代理服务器。
export const http = defineConfig({
trustProxy: () => true
})
配置查询字符串解析器
来自请求 URL 的查询字符串使用 qs 模块进行解析。你可以在 config/app.ts 文件中配置解析器设置。
查看列表 了解所有可用的选项。
export const http = defineConfig({
qs: {
parse: {
},
}
})
表单方法欺骗
HTML 表单的表单方法只能设置为 GET 或 POST,这使得利用 restful HTTP 方法 变得不可能。
不过,AdonisJS 允许你通过表单方法欺骗来绕过这个限制。表单方法欺骗是一个花哨的说法,即通过 _method 查询字符串来指定表单方法。
要使方法欺骗生效,你必须将表单 action 设置为 POST,并在 config/app.ts 文件中启用该功能。
export const http = defineConfig({
allowMethodSpoofing: true,
})
启用后,你可以像下面这样欺骗表单方法。
<form method="POST" action="/articles/1?_method=PUT">
<!-- 更新表单 -->
</form>
<form method="POST" action="/articles/1?_method=DELETE">
<!-- 删除表单 -->
</form>
扩展 Request 类
你可以使用宏(macro)或 getter 为 Request 类添加自定义属性。如果你还不熟悉宏的概念,请务必先阅读 扩展 AdonisJS 指南。
import { Request } from '@adonisjs/core/http'
Request.macro('property', function (this: Request) {
return value
})
Request.getter('property', function (this: Request) {
return value
})
由于宏和 getter 是在运行时添加的,你必须让 TypeScript 知道它们的类型。
declare module '@adonisjs/core/http' {
export interface Request {
property: valueType
}
}