请求

未匹配的标注

请求实例(http request) 请求对象 包含当前请求的数据。 当前请求包含这些数据: request body, uploaded files, cookies 等等。
你可以在路由、中间件、异常处理程序中访问到 HTTP context 对象的request对象。

Route.get('/', (ctx) => {
  console.log(ctx.request.url())
})

使用解构方式:

Route.get('/', async ({ request }) => {
  console.log(request.url())
})

URL中的查询字符串和路由参数

可以使用 request.qs() 函数,得到查询字符串

Route.get('/', async ({ request }) => {
  /*
   * URL: /?username=virk
   * qs: { username: 'virk' }
   */
  request.qs()
})

调用 request.params() 函数,得到请求参数。

Route.get('/posts/:id/:slug', async ({ request }) => {
  /*
   * URL: /posts/1/hello-world
   * Params: { id: '1', slug: 'hello-world' }
   */
  request.params()
})

你还可以使用 request.param 函数访问单个参数。

request.param('id')

// 可以设置一个默认值(可选)
request.param('id', 1)

请求体(Request body)

你可以使用 request.body 函数访问请求体。

Route.post('/', async ({ request }) => {
  request.body()
})

request.all

当然,你也可以使用 request.all 函数,它返回请求正文(request body)和请求查询字符串的合并副本。

request.all()

request.input

你可以使用 request.input 函数读取读取单个输入字段的值。该方法还支持使用点(.)嵌套取值。

request.input('title')

// 读嵌套的值
request.input('user.profile.username')

当实际值为 nullundefined 时,你还可以定义要返回的默认值。

// 缺失标题时返回 "Hello world" 
request.input('title', 'Hello world')

request.only/request.except

你可以使用 request.onlyrequest.except 方法从请求主体中挑选/过滤特定键。

// 特定筛选
const body = request.only(['title', 'description'])

// 过滤
const body = request.except(['submit', 'csrf_token'])

主体解析 & 支持的内容类型

请求正文使用预先配置的 bodyparser 中间件进行解析。它在 start/kernel.ts 文件中注册为全局中间件。

// 文件名: start/kernel.ts
Server.middleware.register([
  () => import('@ioc:Adonis/Core/BodyParser')
])

bodyparser 的配置存储在 config/bodyparser.ts 文件中。配置文件是自文档型的,因此请随时熟悉所有可用的选项。

将空字符串转换为 null

HTML 表单为没有值的输入字段提交一个空字符串。你可以通过启用 convertEmptyStringsToNull 标记将所有空字符串值标准化为 null

注意:该选项仅适用于 multiparturlencoded 表单提交。

// 文件名: config/bodyparser.ts
{
  form: {
    // ... 其余的配置
    convertEmptyStringsToNull: true
  },

  multipart: {
    // ... 其余的配置
    convertEmptyStringsToNull: true
  }
}

支持的内容类型

bodyparser 能够解析以下内容类型。

JSON

JSON 解析器能处理会发送具有以下内容类型之一的 JSON 字符串的请求。

  • application/json
  • application/json-patch+json
  • application/vnd.api+json
  • application/csp-report

你可以在 config/bodyparser.ts 文件内的 json.types 数组中添加更多内容类型,JSON 解析器也会处理它们。


URL 编码

使用 URL 编码解析器解析带有 content-type='application/x-www-form-urlencoded' 的 URL 编码字符串的请求。


混合内容

content-type='multipart/form-data' 的混合内容请求使用混合内容解析器进行解析。请务必阅读有关 文件上传 的指南以查看所有可用的配置选项。


原始内容

content-type='text/*' 的所有请求都使用默认解析器读取。你可以在中间件或路由处理程序中进一步处理原始字符串。

你可以使用默认解析器来处理自定义/不受支持的内容类型。例如

注册自定义内容类型

// 文件名: config/bodyparser.ts
{
  raw: {
    // ...
    types: ['text/*', 'my-custom-content-type']
  }
}

创建一个中间件来进一步解析内容类型

Route
  .get('/', ({ request }) => {
    console.log(request.all())
  })
  .middleware(async ({ request }, next) => {
    const contentType = request.header('content-type')

    if (contentType === 'my-custom-content-type') {
      const body = request.raw()
      const parsed = someCustomParser(body)
      request.updateBody(parsed)
    }

    await next()
  })

请求路由

request 类保存 HTTP 请求的当前匹配路由,你可以通过以下方式访问它:

Route.get('/', ({ request }) => {
  /**
   * 路由匹配
   */
  console.log(request.route.pattern)

  /**
   * 处理路由请求的处理器
   */
  console.log(request.route.handler)

  /**
   * 附加到路由的中间件
   */
  console.log(request.route.middleware)

  /**
   * 路由名称(如果路由被命名,则存在)
   */
  console.log(request.route.name)
})

你还可以检查当前请求 URL 是否与给定路由匹配。

if (request.matchesRoute('/posts/:id')) {
}

或者传递一个数组来检查多个路由。如果任何路由与当前请求 URL 匹配,则该方法返回 true。

if (request.matchesRoute(['/posts/:id', '/posts/:id/comments'])) {
}

请求 URL

你可以使用 request.url() 方法访问请求 URL。它返回不带域名或端口的路径名。

request.url()

// 包含查询字符串
request.url(true)

request.completeUrl() 方法返回完整的 URL,包括域和端口(如果有)。

request.completeUrl()

// 包含查询字符串
request.completeUrl(true)

请求方法

方法

返回给定请求的 HTTP 方法。启用 表格方法掩饰 时会返回被掩饰的方法。

request.method()

原始方法

intended方法返回实际的 HTTP 方法,而不是掩饰的方法。

request.intended()

请求 id

通过将唯一 ID 关联到每个日志条目,给定 HTTP 请求的请求 ID 可以帮助你调试和跟踪日志

AdonisJS 遵循行业标准,对使用 X-Request-Id 请求头具有最佳支持。

生成请求 ID

打开 config/app.ts 并将 http.generateRequestId 的值设置为 true。

仅当 X-Request-Id 标头未设置时才会生成请求 ID。这允许你在代理服务器级别生成请求 ID,然后在你的 AdonisJS 应用程序中引用它们。

// 文件名: config/app.ts
{
  http: {
    generateRequestId: true
  }
}

获取请求 ID

request.id() 方法通过读取 X-Request-Id 请求头返回请求 ID。流程如下所示:

  • 读取 X-Request-Id 请求头的值。如果存在,则返回该值。
  • 如果在配置中启用了 generateRequestId 标志,则手动生成和设置标头。
  • 当请求头丢失时返回 null,并且 generateRequestId 被禁用。
request.id()

日志中的请求 ID

附加到 HTTP 上下文的日志记录器实例会自动在每个日志语句上设置 request_id 属性。

Route.get('/', ({ logger }) => {
  // { msg: 'hello world', request_id: 'ckk9oliws0000qt3x9vr5dkx7' }
  logger.info('hello world')
})

请求头

request.headers()request.header() 方法允许访问请求标头。

// 获取所有请求头
console.log(request.headers())

header 方法返回单个请求头字段的值。请求头名称不区分大小写

request.header('X-CUSTOM-KEY') === request.header('x-custom-key')

// 具有默认值的请求头
request.header('x-header-name', 'default value')

请求的 IP 地址

request.ip() 方法为 HTTP 请求返回最受信任的 IP 地址。请务必阅读 受信任的代理 部分以了解当你进行代理之后时如何获得正确的 IP 地址。

request.ip()

request.ips() 方法返回一个 IP 地址数组,从最受信任的 IP 地址到最不受信任的 IP 地址。

request.ips()

自定义 IP 检索方式

如果可信代理设置不足以确定正确的 IP 地址,你可以实现自己的 getIp 方法。

打开 config/app.ts 文件,定义 getIp 方法如下:

http: {
  getIp(request) {
    const nginxRealIp = request.header('X-Real-Ip')
    if (nginxRealIp) {
      return nginxRealIp
    }

    return request.ips()[0]
  }
}

表单方法掩饰

标准 HTML 表单不能使用除了 GETPOST 之外的所有 HTTP 动词。因此,例如,这意味着你不能使用 PUT 方法创建表单。

但是,AdonisJS 允许你使用 _method 查询字符串来欺骗 HTTP 方法。在以下示例中,请求将被路由转发到侦听 PUT 请求的路由。

<form method="POST" action="/posts/1?_method=PUT"></form>

表单方法伪造仅适用于:

  • http.allowMethodSpoofing 的值在 config/app.ts 文件中设置为 true 时。
  • 原来的请求方法是 POST

内容协商

内容协商 是一种用于为来自同一 URL 的资源的不同表示提供服务的机制。

发出请求的客户端可以使用不同的 Accept 标头协商 resource representationcharsetlanguageencoding,你可以按如下方式处理它们。

接收内容

request.accepts 方法采用内容类型数组(包括简写形式)并通过检查 Accept 请求头返回最合适的内容类型。你可以在 这里 找到支持的内容类型列表。

Route.get('posts', async ({ request, view }) => {
  const posts = [
    {
      title: 'Adonis 101',
    },
  ]

  switch (request.accepts(['html', 'json'])) {
    case 'html':
      return view.render('posts/index', { posts })
    case 'json':
      return posts
    default:
      return view.render('posts/index', { posts })
  }
})

请求类型

request.types 方法通过检查 Accept 请求头返回内容类型数组。该数组按客户端的偏好排序(最优先排序)。

const types = request.types()

语言

根据 Accept-language 请求头协商请求的语言。

const language = request.language(['fr', 'de'])

if (language) {
  return view.render(`posts/${language}/index`)
}

return view.render('posts/en/index')

多种语言

languages 方法通过检查 Accept-language 请求头返回一组接受的语言。该数组按客户端的偏好排序(最优先排序)。

const languages = request.languages()

编码

使用 Accept-encoding 标头查找最佳编码。

switch (request.encoding(['gzip', 'br'])) {
  case 'gzip':
    return compressAsGzip(someValue)
  case 'br':
    return compressAsBr(someValue)
  default:
    return value
}

多种编码方式

encodings 方法通过检查 Accept-encoding 请求头返回一个接受的编码数组。该数组按客户端的偏好排序(最优先排序)。

const encodings = request.encodings()

字符集

使用 Accept-charset 请求头查找最佳字符集。

const charset = request.charset(['utf-8', 'hex', 'ascii'])
return Buffer.from('hello-world').toString(charset || 'utf-8')

多字符集

charsets 方法通过检查 Accept-charset 请求头返回一个接受的字符集数组。该数组按客户端的偏好排序(最优先排序)。

const charsets = request.charsets()

信任代理

大多数 Node.js 应用程序都经过了 Nginx 或 Caddy 等代理。因此,remoteAddress 的值是代理服务器的 IP 地址,而不是客户端的 IP 地址。

但是,所有代理服务器都设置了 X-Forwaded 请求头以反映请求的原始值,并且你必须通知 AdonisJS 信任代理服务器请求头。

你可以通过修改 config/app.ts 中的 http.trustProxy 值来控制要信任的代理。

// 文件名: config/app.ts
{
  http: {
    trustProxy: proxyAddr.compile(valueComesHere)
  }
}

Ip 地址

你还可以定义一个或一组要信任的代理服务器 IP 地址。

{
  trustProxy: proxyAddr.compile('127.0.0.0/8')
}

// 或者
{
  trustProxy: proxyAddr.compile(['127.0.0.0/8', 'fc00:ac:1ab5:fff::1/64'])
}

你还可以使用以下速记关键字代替 IP 地址。

  • loopback: IPv4 和 IPv6 的回环地址 (比如 ::1127.0.0.1).
  • linklocal: IPv4 和 IPv6 本地链路地址 (比如 fe80::1:1:1:1169.254.0.1).
  • uniquelocal: IPv4 私有地址和 IPv6 唯一本地地址 (比如 fc00:ac:1ab5:fff::1192.168.0.1).

自定义函数

你还可以定义一个自定义函数,该函数根据每个请求返回一个布尔值。

{
  trustProxy: proxyAddr.compile((address, index) => {
    return address === '127.0.0.1' || address === '123.123.123.123'
  })
}

使用中的代理请求头

以下来自于请求类中的方法依赖于受信任的代理来返回正确的值。

  • hostname: request.hostname() 的值源自 X-Forwarded-Host 请求头。
  • protocol: request.protocol() 的值源自 X-Forwarded-Proto请求头。
  • ip/ips: request.ips()request.ip()的值来源于 X-Forwaded-For 请求头。但是,http.getIp 配置方法在定义时具有更高优先级。 了解更多

CORS

AdonisJS 内置支持响应 CORSOPTIONS 请求,前提是在 config/cors.ts 文件中启用它。

// 文件名: config/cors.ts
{
  enabled: true,
  // ... 余下的配置
}

配置文件用途广泛。确保浏览了所有选项并阅读相关评论以了解它们的用法。

其他方法和属性

以下是 Request 类上其他可用方法和属性的列表。

主机名

返回请求主机名。如果 代理请求头 是可信的,则 X-Forwarded-Host 的优先级高于 Host 标头。

request.hostname()

ajax

查看请求头 X-Requested-With 是否设置为 'xmlhttprequest'

if (request.ajax()) {
  // 返回 ajax 请求的响应
}

匹配路由

matchesRoute 查找当前请求是否匹配给定路由。该方法接受路由标识符作为唯一参数。标识符可以是路由模式controller.method 名称路由名称

if (request.matchesRoute('posts.show')) {
}

你还可以匹配多个路由。如果返回的 URL 与任何定义的标识符匹配,则该方法返回 true

if (request.matchesRoute(['posts.show', 'posts.edit'])) {
}

is

匹配给定类型并返回请求的最佳匹配内容类型。

内容类型是从 Content-Type 请求头中选择的,并且该请求必须具有请求主体。

const contentType = request.is(['json', 'xml'])

if (contentType === 'json') {
  // 以 JSON 方式处理请求主体
}

if (contentType === 'xml') {
  // 以 XML 方式处理请求主体
}

更新请求主体

updateBody 允许更新请求正文。请求主体始终是一个字符串。

request.updateBody(myCustomPayload)

更新原始请求主体

updateRawBody 允许更新原始请求正文。原始请求主体始终是一个字符串。

request.updateRawBody(JSON.stringify(myCustomPayload))

更新查询字符串

updateQs 允许更新解析的查询字符串的值。

request.updateQs(someCustomParser(request.parsedUrl.query))

原始请求

返回由 bodyparser 解析的请求的原始请求主体。调用 updateBody 方法不会更改原始内容。

request.original()

包含请求主体

hasBody 查找请求是否有正文。 bodyparser 使用此方法在解析请求之前知道请求是否有正文。

if (request.hasBody()) {
  // 解析请求主体
}

扩展请求类

你可以使用 macrosgetters 扩展 Request 类。扩展请求的最佳位置是在自定义服务内部。

打开预设的 providers/AppProvider.ts 文件并在 boot 方法中写入以下代码。

import { ApplicationContract } from '@ioc:Adonis/Core/Application'

export default class AppProvider {
  public static needsApplication = true
  constructor(protected app: ApplicationContract) {}

  public async boot() {
    const Request = this.app.container.use('Adonis/Core/Request')

    Request.macro('wantsJSON', function () {
      const types = this.types()
      return (
        types[0] && (types[0].includes('/json') || types[0].includes('+json'))
      )
    })
  }
}

在上面的例子中,我们在请求类中添加了 wantsJSON 方法。它读取 Accept 标头的值,如果第一个值是 JSON ,则返回 true。

你可以按如下方式使用新添加的方法。

Route.get('/', ({ request }) => {
  if (request.wantsJSON()) {
    return {}
  }
})

通知 TypeScript 该方法

wantsJSON 属性是在运行时添加的,因此 TypeScript 不知道它。为了通知 TypeScript,我们将使用 声明合并 并将属性添加到 RequestContract 接口。

在路径 contracts/request.ts 处创建一个新文件(文件名不重要)并在其中粘贴以下内容。

// 文件名: contracts/request.ts
declare module '@ioc:Adonis/Core/Request' {
  interface RequestContract {
    wantsJSON(): boolean
  }
}

扩展阅读

以下是一些补充资料,可以了解有关本文档未涵盖的主题的更多信息。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/adonisjs/5.x/ht...

译文地址:https://learnku.com/docs/adonisjs/5.x/ht...

上一篇 下一篇
贡献者:4
讨论数量: 0
发起讨论 只看当前版本


暂无话题~