エラー¶
Vapor は Swift の Error
プロトコルをベースにしたエラー処理を採用しています。ルートハンドラは、エラーを throw
するか、失敗した EventLoopFuture
を返すことができます。Swiftの Error
を throw するか返すと、500
ステータスのレスポンスが生成され、エラーがログに記録されます。AbortError
と DebuggableError
は、それぞれ結果として得られるレスポンスとログを変更するために使用できます。エラーの処理は ErrorMiddleware
によって行われます。このミドルウェアはデフォルトでアプリケーションに追加されており、必要に応じてカスタムロジックに置き換えることができます。
Abort¶
Vapor は Abort
というデフォルトのエラー構造体を提供しています。この構造体は AbortError
と DebuggableError
の両方に準拠しています。HTTP ステータスとオプショナルな失敗理由を指定して初期化できます。
// 404 error, default "Not Found" reason used.
throw Abort(.notFound)
// 401 error, custom reason used.
throw Abort(.unauthorized, reason: "Invalid Credentials")
非同期の状況で throw がサポートされていない場合や EventLoopFuture
を返す必要がある場合、例えば flatMap
クロージャ内で、失敗した未来を返すことができます。
guard let user = user else {
req.eventLoop.makeFailedFuture(Abort(.notFound))
}
return user.save()
Vapor にはオプショナルな値を持つ未来をアンラップするためのヘルパーエクステンション unwrap(or:)
が含まれています。
User.find(id, on: db)
.unwrap(or: Abort(.notFound))
.flatMap
{ user in
// Non-optional User supplied to closure.
}
User.find
が nil
を返した場合、提供されたエラーで未来が失敗します。それ以外の場合は、flatMap
に非オプショナルな値が提供されます。async
/ await
を使用している場合は、通常どおりオプショナルを扱うことができます:
guard let user = try await User.find(id, on: db) {
throw Abort(.notFound)
}
Abort Error¶
デフォルトでは、ルートクロージャによって throw されたり返されたりする任意の Swift の Error は 500 Internal Server Error
レスポンスになります。デバッグモードでビルドされた場合、ErrorMiddleware
はエラーの説明を含めます。セキュリティ上の理由から、リリースモードでビルドするとこれが除去されます。
特定のエラーの結果として得られる HTTP
レスポンスステータスや理由を設定するには、それを AbortError
に準拠させます。
import Vapor
enum MyError {
case userNotLoggedIn
case invalidEmail(String)
}
extension MyError: AbortError {
var reason: String {
switch self {
case .userNotLoggedIn:
return "User is not logged in."
case .invalidEmail(let email):
return "Email address is not valid: \(email)."
}
}
var status: HTTPStatus {
switch self {
case .userNotLoggedIn:
return .unauthorized
case .invalidEmail:
return .badRequest
}
}
}
Debuggable Error¶
ErrorMiddleware
は、ルートによって throw されたエラーのログを記録するために Logger.report(error:)
メソッドを使用します。このメソッドは CustomStringConvertible
や LocalizedError
などのプロトコルへの準拠をチェックし、読みやすいメッセージをログに記録します。
エラーログをカスタマイズするために、エラーを DebuggableError
に準拠させることができます。このプロトコルには、固有の識別子、ソースの位置、スタックトレースなど、多くの便利なプロパティが含まれています。これらのプロパティのほとんどはオプショナルであるため、準拠を採用するのは容易です。
DebuggableError
に最適に準拠するためには、エラーは構造体であるべきです。これにより、必要に応じてソースとスタックトレース情報を格納できます。以下は、前述の MyError
列挙型を構造体に更新し、エラーソース情報をキャプチャする例です。
import Vapor
struct MyError: DebuggableError {
enum Value {
case userNotLoggedIn
case invalidEmail(String)
}
var identifier: String {
switch self.value {
case .userNotLoggedIn:
return "userNotLoggedIn"
case .invalidEmail:
return "invalidEmail"
}
}
var reason: String {
switch self.value {
case .userNotLoggedIn:
return "User is not logged in."
case .invalidEmail(let email):
return "Email address is not valid: \(email)."
}
}
var value: Value
var source: ErrorSource?
init(
_ value: Value,
file: String = #file,
function: String = #function,
line: UInt = #line,
column: UInt = #column
) {
self.value = value
self.source = .init(
file: file,
function: function,
line: line,
column: column
)
}
}
DebuggableError
には、エラーのデバッグ性を向上させるために使用できる possibleCauses
や suggestedFixes
など、他にもいくつかのプロパティがあります。より詳細に知りたい場合は、プロトコル自体をご覧ください。
スタックトレース¶
Vaporは、通常のSwiftエラーやクラッシュに対するスタックトレースの表示をサポートしています。
Swift バックトレース¶
Vapor は、Linux 上で致命的なエラーやアサーションの後にスタックトレースを提供するために、SwiftBacktrace ライブラリを使用しています。これが機能するためには、アプリはコンパイル中にデバッグシンボルを含める必要があります。
swift build -c release -Xswiftc -g
エラートレース¶
デフォルトでは、Abort
は初期化されたときに現在のスタックトレースをキャプチャします。カスタムエラータイプは、DebuggableError
に準拠し、StackTrace.capture()
を保存することでこれを実現できます。
import Vapor
struct MyError: DebuggableError {
var identifier: String
var reason: String
var stackTrace: StackTrace?
init(
identifier: String,
reason: String,
stackTrace: StackTrace? = .capture()
) {
self.identifier = identifier
self.reason = reason
self.stackTrace = stackTrace
}
}
アプリケーションのログレベルが .debug
以下に設定されている場合、エラースタックトレースはログ出力に含まれます。
ログレベルが .debug
より大きい場合、スタックトレースはキャプチャされません。この挙動を変更するには、configure
内で StackTrace.isCaptureEnabled
を手動で設定してください。
// Always capture stack traces, regardless of log level.
StackTrace.isCaptureEnabled = true
エラーミドルウェア¶
ErrorMiddleware
は、デフォルトでアプリケーションに追加される唯一のミドルウェアです。このミドルウェアは、ルートハンドラーによって投げられたり返されたりした Swift のエラーを HTTP レスポンスに変換します。このミドルウェアがない場合、投げられたエラーは応答なしに接続が閉じられることになります。
AbortError
と DebuggableError
が提供するものを超えてエラー処理をカスタマイズするには、ErrorMiddleware
を独自のエラー処理ロジックで置き換えることができます。これを行うには、まず app.middleware
を空の設定に設定して、デフォルトのエラーミドルウェアを削除します。その後、独自のエラー処理ミドルウェアをアプリケーションに最初のミドルウェアとして追加します。
// Remove all existing middleware.
app.middleware = .init()
// Add custom error handling middleware first.
app.middleware.use(MyErrorMiddleware())
エラー処理ミドルウェアの 前に 置くべきミドルウェアはほどんどありません。注目すべき例外は CORSMiddleware
です。