alpha_cod
alpha_cod

Reputation: 2033

Elm read HTTP response body for non-200 response

How to read HTTP response body for a non 200 HTTP status

getJson : String -> String -> Effects Action
getJson url credentials =
  Http.send Http.defaultSettings
    { verb = "GET"
    , headers = [("Authorization", "Basic " ++ credentials)]
    , url = url
    , body = Http.empty
    }
    |> Http.fromJson decodeAccessToken
    |> Task.toResult
    |> Task.map UpdateAccessTokenFromServer
    |> Effects.task

The above promotes the error from

Task.toResult : Task Http.Error a -> Task x (Result Http.Error a)

The value of which becomes

(BadResponse 400 ("Bad Request"))

My server responds with what is wrong with the request as a JSON payload in the response body. Please help me retrieve that from the Task x a into ServerResult below.

type alias ServerResult = { status : Int, message : String }

Upvotes: 1

Views: 1448

Answers (1)

Chad Gilbert
Chad Gilbert

Reputation: 36375

The Http package (v3.0.0) does not expose an easy way to treat HTTP codes outside of the 200 to 300 range as non-error responses. Looking at the source code, the handleResponse function is looking between the hardcoded 200 to 300 range

However, with a bit of copy and pasting from that Http package source code, you can create a custom function to replace Http.fromJson in order to handle HTTP status codes outside the normal "success" range.

Here's an example of the bare minimum you'll need to copy and paste to create a custom myFromJson function that acts the same as the Http package except for the fact it also treats a 400 as a success:

myFromJson : Json.Decoder a -> Task Http.RawError Http.Response -> Task Http.Error a
myFromJson decoder response =
  let decode str =
        case Json.decodeString decoder str of
          Ok v -> Task.succeed v
          Err msg -> Task.fail (Http.UnexpectedPayload msg)
  in
      Task.mapError promoteError response
        `Task.andThen` myHandleResponse decode


myHandleResponse : (String -> Task Http.Error a) -> Http.Response -> Task Http.Error a
myHandleResponse handle response =
  if (200 <= response.status && response.status < 300) || response.status == 400 then

      case response.value of
        Http.Text str ->
            handle str

        _ ->
            Task.fail (Http.UnexpectedPayload "Response body is a blob, expecting a string.")

  else

      Task.fail (Http.BadResponse response.status response.statusText)

-- copied verbatim from Http package because it was not exposed
promoteError : Http.RawError -> Http.Error
promoteError rawError =
  case rawError of
    Http.RawTimeout -> Http.Timeout
    Http.RawNetworkError -> Http.NetworkError

Again, that code snippet is almost entirely copy and pasted except for that 400 status check. Copying and pasting like that is usually a last resort, but because of the library restrictions, it seems to be one of your only options at this point.

Upvotes: 3

Related Questions