请求

未匹配的标注

请求实例 (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
发起讨论 只看当前版本


暂无话题~