-
Notifications
You must be signed in to change notification settings - Fork 59
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
HTTP responses don't have information from body on non-success status #132
Comments
Can you please share the sample? I saw But why would you want to have strongly typed (deserialized) body of failed http request? |
Released |
Was this applied to the Using the I was hoping for something where if eg. I have the following spec:
then I would have the following exceptions generated:
which would each throw when I get their respective status back, and I'd then be able to read the structured error data from the response. In my use case, it's much easier for me to diagnose a 400 if I can log what the validation errors were that went wrong, and also my program can respond differently to the 400 if it can consume the extra metadata about the failure that's returned. |
if I got it correctly, it looks like by design in OpenAPI.NET =( Also I pushed version It is possible in your case to update names in schema? It is manually created or generated by tool like Swashbuckle? |
Urgh, that's annoying.
The spec in question is out of my control but I've requested its owners if they can make a workaround due to this issue. I'll let you know the progress on that.
Yep, would it be possible to change this to be as described above, generated based off the spec? I'm not sure how much work this is but it would certainly be valuable. At the moment the |
I asked about the schema because if it generated by Swashbuckle there is new version for aspnetcore https://github.com/domaindrivendev/Swashbuckle.AspNetCore where MS helped to migrate to OpenApi.NET. So they use the same object model and always generate compliant schemas (v2 and v3).
Let's think together, I am not sure that it is best option. If I throw new type of exception for every error of every method your code will crazy because you will have to catch all of them (and somehow find them, maybe even open schema and take a look how errors are documented). It may be hard to do and lead to ugly code. One exception type does not give you strongly types access to error payload, but you need to handle only one case "call not succeeded". If you plan to just log error information, |
Yeah, probably a preferred and more functional way would be to have a union type returned like
but alas, provided union types can't be done 😢 (fsharp/fslang-suggestions#154). You could get around the issues above with a tweak to the multiple exception route though - for the issue of trying to catch them all, they could all inherit from
To solve the discoverability issue, could these custom exception types all be nested in a hierarchy (since we can now open static classes)?
(writing in C# as nested types aren't in F#, but I assume it's still possible to generate them via type providers?) |
I understand this doesn't help with getting the error as defined in the schema, but if you want a workaround to at least get the body when it throws, you can write some middleware like this: exception private UnreliableApiException of HttpStatusCode * string
type ErrorRaisingHandler (inner) =
inherit DelegatingHandler (inner)
// https://wizardsofsmart.wordpress.com/2014/05/13/how-to-use-base-sendasync-in-f-delegatinghandler/
member private x.SendAsync' (request, cancellationToken) = base.SendAsync(request, cancellationToken)
override this.SendAsync (request, cancellationToken) = Async.StartAsTask <| async {
let! result = Async.AwaitTask <| this.SendAsync' (request, cancellationToken)
if result.IsSuccessStatusCode then return result else
let! body = Async.AwaitTask <| result.Content.ReadAsStringAsync ()
raise <| UnreliableApiException (result.StatusCode, body)
return result
} and use it like: let http = HttpClientHandler () :> HttpMessageHandler |> ErrorRaisingHandler |> HttpClient
let client = OpenApiClientProvider<"my-schema.json">.Client(http) (Or you could implement an |
@NickDarvey Yeah true...it does feel a bit hacky to me though and I don't feel great about it, sneaking an exception in the handler pipeline to divert control flow and circumvent the logic in the provided wrapper. (Although I'm not aware of much else besides status code handling that this dodges at the moment, in general over time what else might it inadvertently circumvent, if more post-processing options are added in SwaggerProvider?) Still yep thanks, it's a good workaround in the meantime until support is added in SwaggerProvider itself. |
I went for mix of Nick's answer and LoggingHandler. let unSuccessStatusCode = new Event<_>() // id, status, content
type CustomErrorHandler(messageHandler) =
inherit DelegatingHandler(messageHandler)
override __.SendAsync(request, cancellationToken) =
let resp = base.SendAsync(request, cancellationToken)
async {
let! result = resp |> Async.AwaitTask
if not result.IsSuccessStatusCode then
let! cont = result.Content.ReadAsStringAsync() |> Async.AwaitTask
let hasId, idvals = request.Headers.TryGetValues("RequestId") // Some unique id
unSuccessStatusCode.Trigger(
(if not hasId then None else idvals |> Seq.tryHead),
result.StatusCode,
cont)
return result
} |> Async.StartAsTask ...and then just parse the content with FSharp.Data.JsonProvider |
@sergey-tihon, Hawaii (which is not a TP, but instead a code generator) solves the issue with returning a This seems to make more sense in general, as quite a few apis use 404 for stuff like “customer not found” after a query and being able to inspect result and have a concrete object in the Error type would make this much simpler than injecting an extra response handler. Not sure how hard it would be to do, but adding (I’m writing this after spending a day or so trying to find where the body of the error went with OpenApiProvider, and this thread is not the most discoverable ;). Love the TP though, but this would be awesome to have!) |
We use ProblemDetails for Errors. It would be awesome if the OpenApiException had a field for that. |
OpenApi Generator creates an ApiException for all error cases and that has the raw content. |
When the response of a call doesn't have a success status code, the call throws an
HttpRequestException
with no context for what went wrong beyond the actual status code.EnsureSuccessStatusCode
is called and the content of the response is lost.In some (most?) OpenAPI schemas, the shape of the response for non-success status codes is also documented and a schema is provided for each media type, just as it is for success codes. SwaggerProvider should see if any are present, and if so generate custom exceptions which can be thrown containing the deserialised body when such statuses are returned.
This will greatly help with diagnosing errors that come from a service, when eg. they provide further info than just "bad request" (what about my request was bad?).
The text was updated successfully, but these errors were encountered: