Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elm 0.19: How to obtain request body when receiving BadStatus with elm/http 2.0.0

Tags:

elm/http 1.0.0 defined Http.Error as

type Error
    = BadUrl String
    | Timeout
    | NetworkError
    | BadStatus (Response String)
    | BadPayload String (Response String)

but 2.0.0 changed it to

type Error
    = BadUrl String
    | Timeout
    | NetworkError
    | BadStatus Int
    | BadBody String

When receiving BadStatus I cannot obtain the body of the request, only the status code. In the docs Evan suggests a solution for this, but I don't understand how to make it work.

If we defined our own expectJson similar to

expectJson : (Result Http.Error a -> msg) -> D.Decoder a -> Expect msg
expectJson toMsg decoder =
  expectStringResponse toMsg <|
    \response ->
      case response of
        Http.BadStatus_ metadata body ->
          Err (Http.BadStatus metadata.statusCode)
        ...

Then we have access to the metadata and body, but how do I use them? Should I define my own myBadStatus and return that instead?

Http.BadStatus_ metadata body ->
  Err (myBadStatus metadata.statusCode body)

Would this work?

What I need is to convert the following code:

myErrorMessage : Http.Error -> String
myErrorMessage error =
    case error of
        Http.BadStatus response ->
            case Decode.decodeString myErrorDecoder response.body of
                Ok err ->
                    err.message
                Err e ->
                    "Failed to parse JSON response."
        ...

Thank you.

like image 997
Francisco Dibar Avatar asked Jan 26 '19 14:01

Francisco Dibar


1 Answers

Edit 22/4/2019: I updated this answer for version 2.0+ of http-extras which has some API changes. Thanks to Berend de Boer for pointing this out!

The answer below gives a solution using a package I wrote (as per request), but you don't have to use the package! I wrote an entire article on how to extract detailed information from an HTTP response, it includes multiple Ellie examples that don't require the package, as well as an example that uses the package.


As Francesco mentioned, I created a package for exactly this purpose, using a similar approach described in the question: https://package.elm-lang.org/packages/jzxhuang/http-extras/latest/.

Specifically, the module to use Http.Detailed. It defines an Error type that keeps the original body around on error:

type Error body
    = BadUrl String
    | Timeout
    | NetworkError
    | BadStatus Metadata body Int
    | BadBody Metadata body String

Make a request like so:

type Msg
    = MyAPIResponse (Result (Http.Detailed.Error String) ( Http.Metadata, String ))

sendRequest : Cmd Msg
sendRequest =
    Http.get
        { url = "/myapi"
        , expect = Http.Detailed.expectString MyAPIResponse

In your update, handle the result including decoding the body when it is BadStatus:

update msg model =
    case msg of
        MyAPIResponse httpResponse ->
            case httpResponse of
                Ok ( metadata, respBody ) ->
                    -- Do something with the metadata if you need! i.e. access a header

                Err error ->
                    case error of
                        Http.Detailed.BadStatus metadata body statusCode ->
                            -- Try to decode the body the body here...

                        ...

        ...

Thanks the Francisco for reaching out to me about this, hopefully this answer helps anyone who faces the same problem as OP.

like image 109
Jeffrey Huang Avatar answered Oct 30 '22 23:10

Jeffrey Huang