Todo app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

91 lines
3.5 KiB

import Vapor
// Modified from Vapor.ErrorMiddleware
public final class CustomErrorMiddleware: Middleware {
// Default response
public struct ErrorResponse: Codable {
var error: Bool
var reason: String
}
public init(environment: Environment) {
self.environment = environment
}
public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
next.respond(to: request).flatMapErrorThrowing { error in
self.makeResponse(with: request, reason: error)
}
}
// -------------------------------------
private func makeResponse(with req: Request, reason error: Error) -> Response {
let reason: String
let status: HTTPResponseStatus
var headers: HTTPHeaders
let source: ErrorSource
// Inspect the error type and extract what data we can.
switch error {
case let debugAbort as (DebuggableError & AbortError):
(reason, status, headers, source) = (debugAbort.reason, debugAbort.status, debugAbort.headers, debugAbort.source ?? .capture())
case let abort as AbortError:
(reason, status, headers, source) = (abort.reason, abort.status, abort.headers, .capture())
case let debugErr as DebuggableError:
(reason, status, headers, source) = (debugErr.reason, .internalServerError, [:], debugErr.source ?? .capture())
default:
// In debug mode, provide the error description; otherwise hide it to avoid sensitive data disclosure.
reason = environment.isRelease ? "Something went wrong." : String(describing: error)
(status, headers, source) = (.internalServerError, [:], .capture())
}
// Report the error
req.logger.report(error: error, file: source.file, function: source.function, line: source.line)
let body = makeResponseBody(with: req, reason: reason, status: status, headers: &headers)
// Create a Response with appropriate status
return Response(status: status, headers: headers, body: body)
}
private func makeResponseBody(with req: Request, reason: String, status: HTTPResponseStatus,
headers: inout HTTPHeaders) -> Response.Body {
let body: Response.Body
if let acceptHeader = req.headers.first(name: "Accept"),
acceptHeader == "application/json" {
// Attempt to serialize the error to JSON
do {
let encoder = try ContentConfiguration.global.requireEncoder(for: .json)
var byteBuffer = req.byteBufferAllocator.buffer(capacity: 0)
try encoder.encode(ErrorResponse(error: true, reason: reason), to: &byteBuffer, headers: &headers)
body = .init(
buffer: byteBuffer,
byteBufferAllocator: req.byteBufferAllocator
)
} catch {
body = .init(string: "Oops: \(String(describing: error))\nWhile encoding error: \(reason)",
byteBufferAllocator: req.byteBufferAllocator)
headers.contentType = .plainText
}
}
else {
// Attempt to render the error to a page
let statusCode = String(status.code)
body = .init(string: MainLayout(title: "Error \(statusCode))") {
ErrorPage(status: statusCode, reason: reason)
}.render())
headers.contentType = .html
}
return body
}
private let environment: Environment
}