question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

HTTP responses don't have information from body on non-success status

See original GitHub issue

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?).

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:11 (7 by maintainers)

github_iconTop GitHub Comments

4reactions
NickDarveycommented, Nov 22, 2019

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 ErrorLoggingHandler which accepts a delegate on construction if you just want to log.)

2reactions
adamjones1commented, Nov 20, 2019

Yeah, probably a preferred and more functional way would be to have a union type returned like

type MyOperationResult = 
    | Success of MyOperationSuccessSchema
    | NotFound of MyOperationNotFoundSchema
    | BadRequest of MyOperationBadRequestSchema
    | ... etc

but alas, provided union types can’t be done 😢 (https://github.com/fsharp/fslang-suggestions/issues/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 OpenApiException as it’s implemented now. Then clients who don’t care about the response body or distinguishing between different codes can just catch OpenApiException, and clients who only care for a subset of them can do eg.

try
    client.MyOperation(...)
with
| :? MyOperationNotFoundException as ex -> failwithf "Not found: %A" ex.Body
| :? OpenApiException as ex -> failwith "response code wasn't success" // catch all other non-success status codes (BadRequest etc)

To solve the discoverability issue, could these custom exception types all be nested in a hierarchy (since we can now open static classes)?

public static class Exceptions
{
    public static class ForMyOperation
    {
        public class MyOperationNotFoundException : OpenApiException { ... }
        public class MyOperationBadRequestException : OpenApiException { ... }
        ...
    }
    public static class ForSomeOtherOperation
    {
        public class SomeOtherOperationInternalServerErrorException : OpenApiException { ... }
        public class SomeOtherOperationForbiddenException : OpenApiException { ... }
        ...
    }
    ...
}

(writing in C# as nested types aren’t in F#, but I assume it’s still possible to generate them via type providers?)

Read more comments on GitHub >

github_iconTop Results From Across the Web

What is the proper REST response code for a valid request ...
It only means that the server has no current representation of the requested resource available, and this even may be only temporary. So ......
Read more >
Which http status code to use for no search results found?
I was implementing a search REST API and was thinking about the no results status. There are a couple of options that are...
Read more >
Should an HTTP API always return a body?
I would not return simply a success status in the response, the HTTP error code only signals success or error. I'd only include...
Read more >
Every Important HTTP Status Code Explained
HTTP status codes are vital to creating an API and in this article I will explain all the important HTTP status codes and...
Read more >
A Complete Guide and List of HTTP Status Codes
A complete list of HTTP status codes with explaination of what they are, why they occur and what you can do to fix...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found