Skip to content

响应

Response 类的实例用于响应 HTTP 请求。AdonisJS 支持发送 HTML 片段JSON 对象等。响应实例可以使用 ctx.response 属性访问。

发送响应

发送响应的最简单方式是从路由处理程序返回一个值。

ts
import router from '@adonisjs/core/services/router'

router.get('/', async () => {
  /** 纯字符串 */
  return '这是首页。'

  /** Html 片段 */
  return '<p> 这是首页 </p>'

  /** JSON 响应 */
  return { page: 'home' }

  /** 转换为 ISO 字符串 */
  return new Date()
})

除了从路由处理程序返回值,你还可以使用 response.send 方法显式设置响应体。但是,多次调用 response.send 方法会覆盖旧的响应体,只保留最新的。

ts
import router from '@adonisjs/core/services/router'

router.get('/', async ({ response }) => {
  /** 纯字符串 */
  response.send('这是首页')

  /** Html 片段 */
  response.send('<p> 这是首页 </p>')

  /** JSON 响应 */
  response.send({ page: 'home' })

  /** 转换为 ISO 字符串 */
  response.send(new Date())
})

可以使用 response.status 方法设置响应的自定义状态码。

ts
response.status(200).send({ page: 'home' })

// 发送空的 201 响应
response.status(201).send('')

流式内容

response.stream 方法允许将流管道传输到响应。该方法在完成后会在内部销毁流。

response.stream 方法不设置 content-typecontent-length 头;你必须在流式传输内容之前显式设置它们。

ts
import router from '@adonisjs/core/services/router'

router.get('/', async ({ response }) => {
  const image = fs.createReadStream('./some-file.jpg')
  response.stream(image)
})

发生错误时会向客户端发送 500 状态码。但是,你可以通过定义回调作为第二个参数来自定义错误码和消息。

ts
const image = fs.createReadStream('./some-file.jpg')

response.stream(image, () => {
  const message = '无法提供文件。请重试'
  const status = 400

  return [message, status]
})

下载文件

当你想从磁盘流式传输文件时,我们建议使用 response.download 方法而不是 response.stream 方法。这是因为 download 方法会自动设置 content-typecontent-length 头。

ts
import app from '@adonisjs/core/services/app'
import router from '@adonisjs/core/services/router'

router.get('/uploads/:file', async ({ response, params }) => {
  const filePath = app.makePath(`uploads/${params.file}`)

  response.download(filePath)
})

你可以选择为文件内容生成 Etag。使用 Etag 将帮助浏览器重用之前请求的缓存响应(如果有)。

ts
const filePath = app.makePath(`uploads/${params.file}`)
const generateEtag = true

response.download(filePath, generateEtag)

类似于 response.stream 方法,你可以通过定义回调作为最后一个参数来发送自定义错误消息和状态码。

ts
const filePath = app.makePath(`uploads/${params.file}`)
const generateEtag = true

response.download(filePath, generateEtag, (error) => {
  if (error.code === 'ENOENT') {
    return ['文件不存在', 404]
  }

  return ['无法下载文件', 400]
})

强制下载文件

response.attachment 方法类似于 response.download 方法,但它通过设置 Content-Disposition 头强制浏览器将文件保存到用户计算机上。

ts
import app from '@adonisjs/core/services/app'
import router from '@adonisjs/core/services/router'

router.get('/uploads/:file', async ({ response, params }) => {
  const filePath = app.makePath(`uploads/${params.file}`)

  response.attachment(filePath, 'custom-filename.jpg')
})

设置响应状态和头

设置状态

你可以使用 response.status 方法设置响应状态。调用此方法将覆盖现有的响应状态(如果有)。但是,你可以使用 response.safeStatus 方法仅在状态为 undefined 时设置状态。

ts
import router from '@adonisjs/core/services/router'

router.get('/', async ({ response }) => {
  /**
   * 将状态设置为 200
   */
  response.safeStatus(200)

  /**
   * 不设置状态,因为它已经设置了
   */
  response.safeStatus(201)
})

设置头

你可以使用 response.header 方法设置响应头。此方法会覆盖现有的头值(如果已存在)。但是,你可以使用 response.safeHeader 方法仅在头为 undefined 时设置头。

ts
import router from '@adonisjs/core/services/router'

router.get('/', async ({ response }) => {
  /**
   * 定义 content-type 头
   */
  response.safeHeader('Content-type', 'text/html')

  /**
   * 不设置 content-type 头,因为它已经设置了
   */
  response.safeHeader('Content-type', 'text/html')
})

你可以使用 response.append 方法追加值到现有头值。

ts
response.append('Set-cookie', 'cookie-value')

response.removeHeader 方法删除现有头。

ts
response.removeHeader('Set-cookie')

重定向

response.redirect 方法返回 Redirect 类的实例。重定向类使用流畅 API 构造重定向 URL。

执行重定向的最简单方式是使用重定向路径调用 redirect.toPath 方法。

ts
import router from '@adonisjs/core/services/router'

router.get('/posts', async ({ response }) => {
  response.redirect().toPath('/articles')
})

重定向类还允许从预注册的路由构造 URL。redirect.toRoute 方法接受路由标识符作为第一个参数,路由参数作为第二个参数。

ts
import router from '@adonisjs/core/services/router'

router.get('/articles/:id', async () => {}).as('articles.show')

router.get('/posts/:id', async ({ response, params }) => {
  response.redirect().toRoute('articles.show', { id: params.id })
})

重定向回上一页

在表单提交期间验证错误时,你可能想将用户重定向回上一页。你可以使用 redirect.back 方法来实现。

ts
response.redirect().back()

重定向状态码

重定向响应的默认状态是 302;你可以通过调用 redirect.status 方法更改它。

ts
response.redirect().status(301).toRoute('articles.show', { id: params.id })

带查询字符串的重定向

你可以使用 withQs 方法向重定向 URL 追加查询字符串。该方法接受键值对对象并将其转换为字符串。

ts
response.redirect().withQs({ page: 1, limit: 20 }).toRoute('articles.index')

要转发当前请求 URL 的查询字符串,请不带任何参数调用 withQs 方法。

ts
// 转发当前 URL 查询字符串
response.redirect().withQs().toRoute('articles.index')

当重定向回上一页时,withQs 方法将转发上一页的查询字符串。

ts
// 转发当前 URL 查询字符串
response.redirect().withQs().back()

使用错误中止请求

你可以使用 response.abort 方法通过引发异常来结束请求。该方法将抛出 E_HTTP_REQUEST_ABORTED 异常并触发异常处理流程。

ts
router.get('posts/:id/edit', async ({ response, auth, params }) => {
  const post = await Post.findByOrFail(params.id)

  if (!auth.user.can('editPost', post)) {
    response.abort({ message: '无法编辑文章' })
  }

  // 继续其余逻辑
})

默认情况下,异常将创建状态码为 400 的 HTTP 响应。但是,你可以将自定义状态码指定为第二个参数。

ts
response.abort({ message: '无法编辑文章' }, 403)

响应完成后运行操作

你可以使用 response.onFinish 方法监听 Node.js 完成将响应写入 TCP 套接字的事件。在底层,我们使用 on-finished 包,请随时查阅包的 README 文件以获得深入的技术解释。

ts
router.get('posts', ({ response }) => {
  response.onFinish(() => {
    // 清理逻辑
  })
})

响应体序列化

使用 response.send 方法设置的响应体在写入响应到传出消息流之前会被序列化为字符串。

以下是支持的数据类型及其序列化规则列表。

  • 数组和对象使用安全字符串化函数进行字符串化。该方法类似于 JSON.stringify,但删除循环引用并序列化 BigInt(s)
  • 数字和布尔值被转换为字符串。
  • Date 类的实例通过调用 toISOString 方法转换为字符串。
  • 正则表达式和错误对象通过调用 toString 方法转换为字符串。
  • 任何其他数据类型都会导致异常。

内容类型推断

序列化响应后,响应类自动推断并设置 content-typecontent-length 头。

以下是我们遵循的设置 content-type 头的规则列表。

  • 对于数组和对象,内容类型设置为 application/json
  • 对于 HTML 片段,设置为 text/html
  • JSONP 响应使用 text/javascript 内容类型发送。
  • 对于其他所有内容,内容类型设置为 text/plain

扩展 Response 类

你可以使用宏或 getter 向 Response 类添加自定义属性。如果你不熟悉宏的概念,请先阅读扩展 AdonisJS 指南

ts
import { Response } from '@adonisjs/core/http'

Response.macro('property', function (this: Response) {
  return value
})
Response.getter('property', function (this: Response) {
  return value
})

由于宏和 getter 是在运行时添加的,你必须告知 TypeScript 它们的类型。

ts
declare module '@adonisjs/core/http' {
  export interface Response {
    property: valueType
  }
}