import Hummingbird /// Serves a custom error page for requests that match neither a route nor a static file. /// /// Placed ahead of `FileMiddleware` in the middleware chain, it catches the `.notFound` error /// that bubbles up when no file exists for the requested path and responds with the rendered /// ``ErrorPage`` and a `404 Not Found` status. The page is served in the language stored on the /// context by ``LocalizationMiddleware``, falling back to the default language. public struct NotFoundMiddleware { // MARK: Properties /// The error page, rendered once per supported language and reused for every not-found response. private let responses: LocalizedHTMLCollectionResponse // MARK: Initializers /// Creates a not-found middleware. public init() { self.responses = .init( status: .notFound ) { ErrorPage(locale: $0) } } } // MARK: - RouterMiddleware extension NotFoundMiddleware: RouterMiddleware { // MARK: Functions /// Passes the request down the chain, rendering the error page if it results in a not-found /// response. /// /// Any error other than `.notFound` is rethrown unchanged. /// - Parameters: /// - request: the incoming request. /// - context: the context the request is resolved against. /// - next: the next responder in the middleware chain. /// - Returns: the downstream response, or the rendered ``ErrorPage`` with a `404 Not Found` status. /// - Throws: any non-not-found error thrown downstream. public func handle( _ request: Request, context: Context, next: (Request, Context) async throws -> Response ) async throws -> Response { do { return try await next(request, context) } catch let error { guard let responseError = error as? any HTTPResponseError, responseError.status == .notFound else { throw error } return responses.response( for: context.language ) } } }