Skip to content
This repository has been archived by the owner on May 9, 2018. It is now read-only.

Http.post cannot handle empty response body #5

Closed
srid opened this issue May 19, 2015 · 5 comments
Closed

Http.post cannot handle empty response body #5

srid opened this issue May 19, 2015 · 5 comments

Comments

@srid
Copy link

srid commented May 19, 2015

The single insert API of postgrest returns an empty response body (actual information, a redirect URL, is in one of the response headers).

This doesn't work well with Http.post, because none of the json decoders will successfully parse an empty body. The Json.Decode module doesn't export any function that allows us to bypass empty input:

> Json.Decode.decodeString (Json.Decode.null 0) ""
Err ("Unexpected end of input") : Result.Result String number
> Json.Decode.decodeString (Json.Decode.succeed 0) ""
Err ("Unexpected end of input") : Result.Result String number
>

Solutions

  1. Implement Json.Decode.always : a -> Decoder a and pass it to Http.post (Unlike succeed, always will ignore the input without creating any parse errors).
  2. Implement a emptyDecoder in elm-http, and invoke Http.post Http.emptyDecoder ....

Any thoughts on this? It won't be ideal for me to fork the implementation of post and directly invoke send and handle errors.

srid added a commit to srid/chronicle that referenced this issue May 19, 2015
To workaround,
evancz/elm-http#5

Unfortunately this workaround consistenly triggers a bug with the Elm
task library,
https://github.com/elm-lang/core/issues/240
@evancz
Copy link
Owner

evancz commented May 20, 2015

The Http.send function is meant to be general enough that it can handle cases like this. I would use a function like this:

singleInsert : Task Error Response
singleInsert =
  send defaultSettings
    { verb = "POST"
    , headers = []
    , url = "/table_name"
    , body = string """{ "col1": "value1", "col2": "value2" }"""
    }
  `andThen` handleResponse

handleResponse response =
    case Dict.get "Location" response.headers of
      Nothing ->
          fail (UnexpectedPayload "response headers should have 'Location' field")

      Just loc ->
          succeed loc

There is a bit more to it, but this is how I'd handle this case. The empty string is not valid JSON, so while it'd make your case simpler, it does not actually makes sense for a library about decoding valid JSON strings.

@evancz
Copy link
Owner

evancz commented Oct 3, 2015

In terms of option number 1, that can be done too.

import Json.Decode as Json

decodeAlways value =
  Json.value `Json.andThen` (\_ -> Json.succeed value)

So I'm gonna close this one. It may make sense to open an issue on core suggesting always for Json.Decode though.

@wjdhamilton
Copy link

wjdhamilton commented Aug 23, 2016

It's probably worth mentioning to those who arrived here looking for advice on how to handle an empty response body that option 1 doesn't work for that scenario. As noted elsewhere Json.Decode is for converting Json payloads into valid Elm values and so given that as @evancz notes above an empty string is not a valid Json value, any Decoder will fail with an empty string. The second example will work, however if you want to return an object of the type Task Error a, as opposed to Task RawError a, then you need to use Task.mapError to convert the RawError instance to to an Error instance. To see how this is done, I suggest looking at the implementation of Http.fromJson in the source files

@hakonrossebo
Copy link

I just want to mention for others having this issue with an empty response. I used promoteError from rgrempel/elm-http-decorators as a simple workaround.

@Spaxe
Copy link

Spaxe commented Apr 20, 2018

Bumped into this. Can't believe it's 2 years old and it's not properly explained in the docs.

I wrote my own request to get around this problem.

safePost : String -> Http.Body -> Http.Request String
-- Because when Http.post expects a JSON, and empty responses aren't valid JSON
-- we have to use this to get around it.
safePost url body =
    Http.request
       { method = "POST"
       , headers = [ header "Content-Type" "application/json" ]
       , url = url
       , body = body
       , expect = Http.expectString
       , timeout = Nothing
       , withCredentials = False
       }

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants