-
Notifications
You must be signed in to change notification settings - Fork 40
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
Add error handling middleware #126
base: main
Are you sure you want to change the base?
Add error handling middleware #126
Conversation
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for picking this up @gayathrisairam!
I've left some initial comments / questions, and a note on how you can run the linters locally.
public protocol HTTPResponseConvertible { | ||
/// HTTP status to return in the response | ||
var httpStatus: HTTPResponse.Status { get } | ||
|
||
/// (Optional) Headers to return in the response | ||
var httpHeaderFields: HTTPTypes.HTTPFields { get } | ||
|
||
/// (Optional) The body of the response to return | ||
var httpBody: OpenAPIRuntime.HTTPBody? { get } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll go and double check the proposal myself but I thought we were going to make these all optional? Right now, we have all three flavours:
httpStatus
is non-optional, with no default implementation;httpHeaderFields
is non-optional, but has a default implementation; andhttpBody
is optional, with a default implementation.
IMO, if we're providing a default implementation in a protocol extension, we should not use optional for the requirement.
I'll go double-check the proposal again, but, regardless of API spelling, is this the semantics we wanted here?
Specifically, if someone adds the ErrorHandlingMiddleware
, don't we want it transform errors that do not conform into 500
and, in debug mode, possibly include something in the header or body?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, if we're providing a default implementation in a protocol extension, we should not use optional for the requirement.
I'm not sure I agree, I think it's good the way it is - header fields is a container that can be empty, so it's non-optional and defaults to empty in the protocol extension. The body, however, is a stateful async sequence, and the middleware API allows returning nil
, so I think it should be optional and default to nil
in the protocol extension.
Specifically, if someone adds the ErrorHandlingMiddleware, don't we want it transform errors that do not conform into 500 and, in debug mode, possibly include something in the header or body?
Yes I also caught this one, I believe here we agreed to always return a response, and use 500 for unconformed errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, if we're providing a default implementation in a protocol extension, we should not use optional for the requirement.
I'm not sure I agree, I think it's good the way it is - header fields is a container that can be empty, so it's non-optional and defaults to empty in the protocol extension. The body, however, is a stateful async sequence, and the middleware API allows returning
nil
, so I think it should be optional and default tonil
in the protocol extension.
Right I think the way it's currently structured and documented is conflating two things. The optionality of whether a user needs to provide an override, and the optionality of the underlying field. We should have these match the target response type in terms of optionality, which, as you point out, they do already. Concretely, the HTTPResponse
has non-optional status and headers (although headers may be empty), and an optional body.
I think what's confusing then is the use of "(Optional)" in the documentation comments for the protocol requirements. How about we remove that term from the documentation and provide defaults for everything in the protocol extension to make it clear, including httpBody
, which will default to nil
and make it clear what the default is in the protocol level documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. I will convert undocumented errors to a 500 response.
What do we want to do with header and body fields? Should we preserve the additional information in ServerError.underlyingError
in the body as an application/json?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we preserve the additional information in ServerError.underlyingError in the body as an application/json?
I'd personally hold off on doing that, as it has the risk of leaking sensitive information in the error without the user explicitly opting into that behavior. I'd leave that up to the user in their HTTPResponseConvertible
to send back as much information as they want, and for unconformed errors to only send 500, without any other headers/body.
Users can always add a logging middleware to at least see the error in logs, and this more conservative default behavior could further motivate users to conform their errors to the protocol and make an explicit decision about the response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's precedent for this being done in debug and not in release. And I'm personally in favour of having this behaviour. Not having it is quite an annoying developer experience IMO.
I'd also propose making it configurable and defaulting to off. But it should be a backwards compatible change so happy to defer it altogether for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to leave it out of this PR, however I would be supportive of a subsequent PR adding it as an explicit option. I really dislike the behavior being controlled by debug vs release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, @gayathrisairam feel free to break this out into a new GitHub issue where we can flesh out how this would look.
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great overall, added a few suggestions for more polish, mainly in the doc comments.
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
…re.swift Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
d235fb3
to
60cb9b9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fixes, added a few more minor suggestions as part of the final pass
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift
Outdated
Show resolved
Hide resolved
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
Co-authored-by: Honza Dvorsky <[email protected]>
@gayathrisairam please rerun the formatter and unit tests, those are failing |
Motivation
Implementation of Improved error handling proposal
Modifications
Added HTTPResponseConvertible protocol
Added ErrorHandlingMiddleware that converts errors confirming to HTTPResponseConvertible to a HTTP response.
Result
The new middleware is an opt-in middleware. So there won't be any change to existing clients.
Clients who wish to have OpenAPI error handling can include the new error middleware in their application.
Test Plan
Added E2E tests to test the new middleware.