レスポンス

レスポンス

responseクラスのインスタンスは、HTTPリクエストに対してレスポンスを返すために使用されます。AdonisJSは、HTMLフラグメントJSONオブジェクトストリームなどの送信をサポートしています。レスポンスインスタンスは、ctx.responseプロパティを使用してアクセスできます。

レスポンスの送信

レスポンスを送信するもっとも簡単な方法は、ルートハンドラから値を返すことです。

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メソッドを複数回呼び出すと、古いボディは上書きされ、最新のボディのみが保持されます。

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メソッドを使用して設定できます。

response.status(200).send({ page: 'home' })
// 空の201レスポンスを送信
response.status(201).send('')

コンテンツのストリーミング

response.streamメソッドを使用すると、ストリームをレスポンスにパイプできます。このメソッドは、ストリームを内部的に破棄します。

response.streamメソッドは、content-typeヘッダとcontent-lengthヘッダを設定しません。ストリーミングコンテンツをストリーミングする前に、これらのヘッダを明示的に設定する必要があります。

import router from '@adonisjs/core/services/router'
router.get('/', async ({ response }) => {
const image = fs.createReadStream('./some-file.jpg')
response.stream(image)
})

エラーが発生した場合、500ステータスコードがクライアントに送信されます。ただし、2番目のパラメータとしてコールバックを定義することで、エラーコードとメッセージをカスタマイズできます。

const image = fs.createReadStream('./some-file.jpg')
response.stream(image, () => {
const message = 'ファイルの提供に失敗しました。もう一度お試しください。'
const status = 400
return [message, status]
})

ファイルのダウンロード

ディスクからファイルをストリーミングする場合は、response.downloadメソッドをresponse.streamメソッドよりも使用することをオススメします。これは、downloadメソッドが自動的にcontent-typeヘッダとcontent-lengthヘッダを設定するためです。

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を使用すると、ブラウザは前回のリクエストからのキャッシュされたレスポンスを再利用するのに役立ちます。

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

response.streamメソッドと同様に、最後のパラメータとしてコールバックを定義することで、カスタムエラーメッセージとステータスコードを送信することもできます。

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ヘッダを設定することで、ブラウザにファイルをユーザーのコンピュータに保存させるように強制します。

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メソッドを使用してレスポンスのステータスを設定できます。このメソッドを呼び出すと、既存のレスポンスステータスが上書きされます(すでに設定されている場合)。ただし、ステータスがundefinedの場合にのみ、response.safeStatusメソッドを使用してステータスを設定できます。

import router from '@adonisjs/core/services/router'
router.get('/', async ({ response }) => {
/**
* ステータスを200に設定します
*/
response.safeStatus(200)
/**
* 既に設定されているため、ステータスは設定されません
*/
response.safeStatus(201)
})

ヘッダの設定

response.headerメソッドを使用してレスポンスヘッダを設定できます。このメソッドは、既存のヘッダ値を上書きします(すでに存在する場合)。ただし、ヘッダがundefinedの場合にのみ、response.safeHeaderメソッドを使用してヘッダを設定できます。

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メソッドを使用して既存のヘッダ値に値を追加できます。

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

response.removeHeaderメソッドを使用して既存のヘッダを削除できます。

response.removeHeader('Set-cookie')

リダイレクト

response.redirectメソッドは、Redirectクラスのインスタンスを返します。リダイレクトクラスは、フルエントAPIを使用してリダイレクトURLを構築します。

リダイレクトを実行するもっとも簡単な方法は、redirect.toPathメソッドをリダイレクトパスとともに呼び出すことです。

import router from '@adonisjs/core/services/router'
router.get('/posts', async ({ response }) => {
response.redirect().toPath('/articles')
})

リダイレクトクラスは、事前に登録されたルートからURLを構築することもできます。redirect.toRouteメソッドは、ルート識別子を第1パラメータとして、ルートパラメータを第2パラメータとして受け入れます。

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メソッドを使用できます。

response.redirect().back()

リダイレクトステータスコード

リダイレクトレスポンスのデフォルトステータスは302です。redirect.statusメソッドを呼び出すことで、ステータスを変更できます。

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

クエリ文字列付きのリダイレクト

withQsメソッドを使用してリダイレクトURLにクエリ文字列を追加できます。このメソッドは、キーと値のペアのオブジェクトを受け入れ、それを文字列に変換します。

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

現在のリクエストURLのクエリ文字列を転送するには、パラメータなしでwithQsメソッドを呼び出します。

// 現在のURLのクエリ文字列を転送
response.redirect().withQs().toRoute('articles.index')

前のページにリダイレクトする場合、withQsメソッドは前のページのクエリ文字列を転送します。

// 現在のURLのクエリ文字列を転送
response.redirect().withQs().back()

エラーによるリクエストの中止

response.abortメソッドを使用して、例外を発生させることでリクエストを終了できます。このメソッドは、E_HTTP_REQUEST_ABORTED例外をスローし、例外処理フローをトリガーします。

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レスポンスを作成します。ただし、2番目のパラメータとしてカスタムステータスコードを指定することもできます。

response.abort({ message: '投稿を編集することはできません' }, 403)

レスポンスの書き込み完了後のアクションの実行

response.onFinishメソッドを使用すると、Node.jsがレスポンスをTCPソケットに書き込み終了したときのイベントをリッスンできます。内部的には、on-finishedパッケージを使用していますので、詳細な技術的な説明についてはパッケージのREADMEファイルを参照してください。

router.get('posts', ({ response }) => {
response.onFinish(() => {
// クリーンアップロジック
})
})

Node.jsのresオブジェクトへのアクセス

response.responseプロパティを使用すると、Node.jsのresオブジェクトにアクセスできます。

router.get('posts', ({ response }) => {
console.log(response.response)
})

レスポンスボディのシリアライズ

response.sendメソッドで設定されたレスポンスボディは、出力メッセージストリームに書き込まれる前に文字列にシリアライズされます。

以下は、サポートされているデータ型とそのシリアライズルールのリストです。

  • 配列とオブジェクトは、安全な文字列化関数を使用して文字列化されます。このメソッドは、JSON.stringifyと似ていますが、循環参照を削除し、BigIntをシリアライズします。
  • 数値とブール値は文字列に変換されます。
  • Dateクラスのインスタンスは、toISOStringメソッドを呼び出して文字列に変換されます。
  • 正規表現とエラーオブジェクトは、toStringメソッドを呼び出して文字列に変換されます。
  • その他のデータ型は例外が発生します。

コンテンツタイプの推論

レスポンスをシリアライズした後、レスポンスクラスは自動的にcontent-typeヘッダとcontent-lengthヘッダを推論して設定します。

以下は、content-typeヘッダを設定するために私たちが従うルールのリストです。

  • 配列とオブジェクトの場合、content-typeapplication/jsonに設定されます。
  • HTMLフラグメントの場合、content-typetext/htmlに設定されます。
  • JSONPレスポンスはtext/javascriptのコンテンツタイプで送信されます。
  • それ以外の場合、content-typetext/plainに設定されます。

Responseクラスの拡張

マクロやゲッターを使用して、Responseクラスにカスタムプロパティを追加できます。マクロの概念についてはじめての場合は、AdonisJSの拡張ガイドを先に読んでください。

import { Response } from '@adonisjs/core/http'
Response.macro('property', function (this: Response) {
return value
})
Response.getter('property', function (this: Response) {
return value
})

マクロとゲッターは実行時に追加されるため、TypeScriptにその型について通知する必要があります。

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