-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to throw errors that won't get transformed to ServerError? #545
Comments
Hi @cantbetrue, throwing a If you want to get access to the original underlying error, for example thrown in your handler, use the Does that address your issue? |
So in a scenario like this, how can I throw ValidationsError directly? struct API: APIProtocol {
func register(_ input: Operations.register.Input) async throws -> Operations.register.Output {
try Components.Schemas.RegisterInput.validate(content: request)
...
}
} |
Which module defines You can throw any errors that are convenient to you in the If you'd like to transform all thrown errors into other errors, you can create a struct ErrorMappingMiddleware: ServerMiddleware {
func intercept(
_ request: HTTPRequest,
body: HTTPBody?,
metadata: ServerRequestMetadata,
operationID: String,
next: @Sendable (HTTPRequest, HTTPBody?, ServerRequestMetadata) async throws -> (HTTPResponse, HTTPBody?)
) async throws -> (HTTPResponse, HTTPBody?) {
do {
return try await next(request, body, metadata)
} catch {
if let error = error as? ServerError {
let transformedError = error.underlyingError // here turn the error you threw in your handler into another error
throw transformedError
} else {
let transformedError = error // here turn other errors into your transformed errors
throw transformedError
}
}
}
} Edit: an end-to-end example of how to use ServerMiddleware: https://github.com/apple/swift-openapi-generator/tree/main/Examples/auth-server-middleware-example |
Anyway, I added your let myMiddlewares = [ErrorMappingMiddleware()]
let transport = VaporTransport(routesBuilder: app)
try API().registerHandlers(on: transport, serverURL: Servers.server1(), middlewares: myMiddlewares) But still errors in that function was thrown as ServerError, I get status code 500 and this as the response: {
"error": true,
"reason": "Server error - cause description: 'Middleware of type 'ErrorMappingMiddleware' threw an error.', underlying error: The operation couldn’t be completed. (Vapor.ValidationsError error 1.), operationID: ..."
} Even if the APIProtocol's function is changed to: func register(_ input: Operations.register.Input) async throws -> Operations.register.Output {
throw Abort(.badRequest, reason: "arbitrary test")
} Still get back 500 status code and all the "Additional info" in error description. I tried to extend Before, in vanilla Vapor, I can easily throw an error as a failed response, with the customized status code and purely cusomizable description, now adding openapi generator makes all that harder. |
Can you provide the implementation of the middleware? How are you transforming the errors? That's what affects the responses you're seeing. |
I pasted in the above import Vapor
import Logging
import OpenAPIVapor
@main
enum Entrypoint {
static func main() async throws {
var env = try Environment.detect()
try configLogger(env: &env)
let app = Application(env)
let transport = VaporTransport(routesBuilder: app)
try API().registerHandlers(on: transport, serverURL: Servers.server1(), middlewares: [ErrorMappingMiddleware()])
defer { app.shutdown() }
do {
try await configure(app)
} catch {
app.logger.report(error: error)
throw error
}
try await app.execute()
}
} Then in the API() struct, the function does nothing but throw an AbortError defined by Vapor https://docs.vapor.codes/basics/errors/?h=abort#abort-error. func register(_ input: Operations.register.Input) async throws -> Operations.register.Output {
throw Abort(.badRequest, reason: "test")
} And the response: |
Edit: see below. |
@cantbetrue Actually, I just played with this a bit more and realized that while the middleware unwraps the handler's ServerError, the rethrown error gets wrapped again as "middleware threw an error" (because only ServerErrors are passed up without being wrapped). What this means is that to achieve what you want, you'll need to turn the |
Ok, confirmed that this does what you want: import OpenAPIRuntime
import OpenAPIVapor
import Vapor
struct Handler: APIProtocol {
func getGreeting(_ input: Operations.getGreeting.Input) async throws -> Operations.getGreeting.Output {
throw Abort(.badRequest, reason: "arbitrary test")
}
}
struct ErrorMappingMiddleware: AsyncMiddleware {
func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
do {
return try await next.respond(to: request)
} catch {
if let error = error as? ServerError {
let transformedError = error.underlyingError // here turn the error you threw in your handler into another error
throw transformedError
} else {
let transformedError = error // here turn other errors into your transformed errors
throw transformedError
}
}
}
}
@main struct HelloWorldVaporServer {
static func main() async throws {
let app = Vapor.Application()
app.middleware.use(ErrorMappingMiddleware())
let transport = VaporTransport(routesBuilder: app)
let handler = Handler()
try handler.registerHandlers(on: transport, serverURL: URL(string: "/api")!)
try await app.execute()
}
} |
Closing as we seem to have found a way to address the ask. Please reopen if you believe this should be handled by Swift OpenAPI Generator. |
Motivation
Is there anyway I can throw errors without being converted to an ServerError in Vapor project? What's the easiest way to do it for every
APIProtocol
function?I want to use this library to generate code, but it limits how I throw errors, it's frustrating
Proposed solution
Maybe put the conversion to ServerError and ClientError to a middleware and give users the choice whether to use it?
Alternatives considered
No response
Additional information
No response
The text was updated successfully, but these errors were encountered: