响应
响应实例(http response) 参考链接 允许针对请求做出一个响应。 在 AdonisJS 中 HTML fragments, JSON objects, streams 都是开箱即用的。
你可以从路由、中间件、异常处理程序中访问到 HTTP context 对象,再得到 response
对象。
Route.get('/', (ctx) => {
ctx.response.send('hello world')
})
发送响应#
最简单的发送响应的方法是,从路由处理程序返回一个值。
Route.get('/', () => {
/** 纯字符串 */
return 'This is the homepage'
/** Html 片段 */
return '<p> This is the homepage </p>'
/** JSON */
return { page: 'home' }
/** 转换成 ISO 字符串 */
return new Date()
})
除了上面简单的方式,你还可以使用 response.send
方法发送响应。
第一个参数是响应体 (与返回值相同)。您可以传递第二个参数(可选的)来生成和设置 etag。
注:你也可以启用 ETag 为所有的响应自动生成,通过在
config/app.ts
文件中的http.etag
属性进行控制。
response.send({ hello: 'world' })
// 使用 etag
response.send({ hello: 'world' }, true)
序列化响应体#
下面是响应序列化的数据类型列表:
- Arrays 和 Objects 序列化使用 安全的字符串化函数。
- 该方法类似于
JSON.stringify
。但删除了循环引用。 - number 和 boolean 被转换为字符串。
- Date 被转换为字符串,通过
toISOString
函数。 - 正则表达式 和 error 对象则调用
toString
来转换。 - 任何其他数据类型都会导致异常。
内容类型推断#
响应类通过检查响应主体自动设置 content-type
和 content-length
响应头部。
注意:自动 content type 响应头仅在请求生命周期中未被显式设置时定义。
- 数组和对象的响应内容类型设置为
application/json
。 - HTML 片段响应为
text/html
。 - JSONP 响应为
text/javascript
。 - 对于其他所有内容,我们将内容类型设置为
text/plain
。
惰性响应#
许多 Node.js 框架会在你调用 response.send
方法后立即响应输出流。然而,AdonisJS 没有做同样的事情。相反,我们会在写入最终响应之前等待路由处理程序和中间件调用完成。
这种方法可以确保最后一次是调用 response.send
。在大多数情况下,此行为根本不会影响对你造成影响。但是,它允许你在中间件内对响应进行进一步处理。
以下是将 camelCase
对象键转换为 snake_case
的示例。
注意:
以下示例不是转换响应的最佳方式。这只是如何对响应进行进一步处理的演示。
import snakeCaseKeys from 'snakecase-keys'
Route
.get('/', async ({ response }) => {
response.send({ fullName: 'Harminder Virk' })
})
.middleware(async ({ response }, next) => {
await next()
/**
* Following code is executed after the route handler.
* Read the middleware guide to learn how it works
*/
const existingBody = response.lazyBody[0]
if (!existingBody || existingBody.constructor !== Object) {
return
}
response.send(snakeCaseKeys(existingBody))
})
路由处理程序使用上述示例中的 response.send
方法写入响应主体。但是,下游中间件会再次使用 response.send
改变响应主体。
由于响应主体是延迟评估的,AdonisJS 将始终通过检查最近的响应主体来设置 content-length 和 content-type 响应头部。
响应状态和响应头#
以下是使用响应头和响应状态的方法。
响应头#
response.header
方法定义 HTTP 响应头。使用此方法会覆盖现有的响应头 (如果有)。
response.header('Content-type', 'text/html')
响应追加#
response.append
方法类似于 header
方法。但是,它会附加到现有的响应头 (如果有)。
response.append('Set-cookie', 'cookie-value')
移除响应头#
response.removeHeader
可以删除现有的响应头。
response.removeHeader('Content-type')
获取响应头#
response.getHeader
方法返回现有响应头的值。
const cookie = response.getHeader('Set-cookie')
安全响应头#
response.safeHeader
方法类似于 header
方法。但是,它仅在不存在响应头时进行定义。
response.safeHeader('Content-type', 'application/json')
响应状态#
response.status
方法定义了 HTTP 响应的状态。你还可以使用 描述性方法 将状态和响应正文一起设置。
response.status(401)
安全状态#
与 status
方法一样,response.status
仅在状态尚未定义时定义。
response.safeStatus(401)
流和文件下载#
AdonisJS 对管道流和文件下载具有很好的支持。我们确保在出现错误时正确清理流。
流#
response.stream
方法允许将流通过管道传输到响应。此方法不会设置 content-type 和 content-length 响应头,你必须手动设置。
const image = fs.createReadStream('./some-file.jpg')
response.stream(image)
如果出现错误,将向客户端发送 500 响应。但是,你可以通过定义 callback
来发送自定义状态代码和消息。回调必须返回一个包含响应消息和响应状态代码的数组。
response.stream(image, (error) => {
return ['Unable to send file', 400]
})
下载#
download
方法通过从磁盘读取文件将文件流式传输到客户端。但是,与流方法不同,download
方法设置了 content-type 和 content-length 响应头。
const filePath = Application.tmpPath('uploads/some-file.jpg')
response.download(filePath)
或者,你还可以为文件定义 ETag。
const filePath = Application.tmpPath('uploads/some-file.jpg')
response.download(filePath, true)
您可以通过传递 callback
作为第三个参数来定义自定义状态码和消息。
const filePath = Application.tmpPath('uploads/some-file.jpg')
response.download(filePath, true, (error) => {
if (error.code === 'ENOENT') {
return ['File does not exists', 404]
}
return ['Cannot download file', 400]
})
附件#
response.attachment
类似于 download
方法。但是,它允许自定义下载的文件名并定义 内容处理 (Content-Disposition) 响应头。
const filePath = Application.tmpPath('uploads/some-file.jpg')
response.attachment(filePath)
// 自定义名称
response.attachment(filePath, 'foo.jpg')
// 自定义处理方式
response.attachment(filePath, 'foo.jpg', 'inline')
重定向#
响应类具有丰富的 API 来处理重定向,包括将用户重定向到路由、重定向回上一页以及转发现有的查询字符串。
您可以使用 response.redirect()
方法获取重定向类的实例。
// 重定向回上一页
response.redirect().back()
// 重定向到 URL
response.redirect().toPath('/some/url')
自定义状态码#
默认情况下,使用 302
状态码。但是,你可以使用.status
方法覆盖。
response.redirect().status(301).toPath('/some/url')
重定向到路由#
你还可以使用.toRoute
方法将请求重定向到命名路由。
response.redirect().toRoute('PostsController.show', { id: 1 })
定义 / 转发查询字符串#
.withQs
允许你转发现有的查询字符串或在重定向期间定义自定义查询字符串。
response
.redirect()
.withQs() // 👈 转发现有的查询字符串
.back()
response
.redirect()
.withQs({ sort: 'id' }) // 👈 自定义
.back()
带参数的查询字符串#
使用自定义对象多次调用.withQs
方法会将对象合并在一起。但是,你可以将其与.clearQs
方法结合使用以清除现有对象。例如:
response
.redirect()
.withQs({ sort: 'id' })
.clearQs()
.withQs({ filters: { name: 'virk' } })
.toPath('/users')
// URL: /users?filters[name]=virk
不带参数的查询字符串#
不带任何参数调用 withQs
方法会将现有查询字符串转发到重定向的 URL。如果你将用户重定向回旧页面,我们将使用 referrer
响应头 URL 中的查询字符串。
response.redirect().withQs().back() // 👈 这里采用了 referrer 响应头
response.redirect().withQs().toPath('/users') // 👈 这里采用了当前 URL
中止与响应#
响应类允许你使用 response.abort
或 response.abortIf
方法中止当前的 HTTP 请求。
中止#
response.abort
方法通过引发 中止异常 (AbortException) 中止当前请求
该方法总共接受两个参数:响应主体和可选状态。
if (!auth.user) {
response.abort('Not authenticated')
// 自定义状态
response.abort('Not authenticated', 401)
}
满足条件,中止#
response.abortIf
方法接受条件并在条件为真时中止请求。
response.abortIf(!auth.user, 'Not authenticated', 401)
满足条件继续,否则中止#
response.abortUnless
方法与 abortIf 方法相反。
response.abortUnless(auth.user, 'Not authenticated', 401)
其他方法和属性#
以下是响应类中可用的其他方法和属性的列表。
结束#
查找响应是否已写入输出流。
if (!response.finished) {
response.send()
}
发送响应头#
Node.js res.headersSent 属性的别名。
等待中#
isPending
属性与 response.finished
属性相反。
if (response.isPending) {
response.send()
}
差异化#
定义 HTTP 变化响应头 的快捷方式。多次调用 vary
方法将追加到现有响应头列表中。
response.vary('Origin')
// 设置多个响应头
response.vary('Accept, User-Agent')
定位#
设置 HTTP 位置响应头 的快捷方式。
response.location('/dashboard')
类型#
设置 HTTP 内容类型响应头 的快捷方式。
response.type('application/json')
你还可以使用关键字来定义内容类型。例如:
response.type('json') // 定义 content-type=application/json
response.type('html') // 定义 content-type=text/html
描述性响应方法#
响应类有一堆描述性方法 (每个 HTTP 状态一个) 来发送响应正文并同时设置状态。例如:
response.badRequest({ error: 'Invalid login credentials' })
response.forbidden({ error: 'Unauthorized' })
response.created({ data: user })
这里有所有可用方法的列表。
扩展响应类#
你可以使用宏或 getter 扩展 Response 类。扩展响应的最佳位置是在自定义服务内部。
打开预设的 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 Response = this.app.container.use('Adonis/Core/Response')
Response.macro('flash', function (messages) {
this.ctx!.session.flash(messages)
return this
})
}
}
在上面的例子中,我们在响应类中添加了 flash
方法。它通过内部调用 session.flash
方法来设置 flash 消息。
您可以按如下方式使用新添加的方法。
Route.post('users', ({ response }) => {
response.flash({ success: 'User created' })
})
通知 TypeScript 该方法#
flash
属性是在运行时添加的,因此 TypeScript 不知道它。为了通知 TypeScript,我们将使用 声明合并 (declaration merging) 并将属性添加到 ResponseContract
接口。
在路径 contracts/response.ts
处创建一个新文件 (文件名不重要) 并将以下内容粘贴到其中。
// 文件名: contracts/response.ts
declare module '@ioc:Adonis/Core/Response' {
interface ResponseContract {
flash(messages: any): this
}
}
扩展阅读#
以下是一些补充资料,用于了解有关本文档未涵盖的主题的更多信息。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。