Search code examples
requesthttp-postelmhttp-getpayload

In Elm, a GET request can't have a body, or is it?


Http.request seems to ignore body when the method is GET

init : () -> ( Model, Cmd Msg )
init _ =
    ( Loading
    , Http.request
        { method = "GET"
        , headers = []
        , url = "http://127.0.0.1"
        , body = Http.stringBody "text/plain" "Hello World!"
        , expect = Http.expectWhatever Sent
        , timeout = Nothing
        , tracker = Nothing
        }
    )

The sent request has no body (when inspected with browser development tool). 🤔

init : () -> ( Model, Cmd Msg )
init _ =
    ( Loading
    , Http.request
        { method = "POST" {- CHANGED TO POST -}
        , headers = []
        , url = "http://127.0.0.1"
        , body = Http.stringBody "text/plain" "Hello World!"
        , expect = Http.expectWhatever Sent
        , timeout = Nothing
        , tracker = Nothing
        }
    )

But when the method is changed to "POST", it works ! The body contains "Hello World!". 🤨

The API I try to communicate with requires an application/json body in a GET request. Help me 😭 !

PS: Here is what the documentations says:

emptyBody : Body

Create an empty body for your Request. This is useful for GET requests and POST requests where you are not sending any data.

Which is not clear, because it can be interpreted in two different ways:

This is useful for GET requests and { POST requests where you are not sending any data } .

Or:

This is useful for { GET requests and POST requests } where you are not sending any data.


Solution

  • Elm actually does add the body to the request, but the browser doesn't send it.

    To see this, compile this Elm program:

    module Main exposing (main)
    
    import Platform
    import Http
    
    main =
        Platform.worker
            { init = \() -> ((), Http.request
                { method = "GET"
                , headers = []
                , url = "https://catfact.ninja/fact"
                , body = Http.stringBody "text/plain" "hi"
                , expect = Http.expectWhatever (\_ -> ())
                , timeout = Nothing
                , tracker = Nothing
                })
            , update = \() () -> ((), Cmd.none)
            , subscriptions = \() -> Sub.none
            }
    

    without optimisation flags to get an index.html file. Part of this file is:

            var xhr = new XMLHttpRequest();
            xhr.addEventListener('error', function() { done($elm$http$Http$NetworkError_); });
            xhr.addEventListener('timeout', function() { done($elm$http$Http$Timeout_); });
            xhr.addEventListener('load', function() { done(_Http_toResponse(request.expect.b, xhr)); });
            $elm$core$Maybe$isJust(request.tracker) && _Http_track(router, xhr, request.tracker.a);
    
            try {
                xhr.open(request.method, request.url, true);
            } catch (e) {
                return done($elm$http$Http$BadUrl_(request.url));
            }
    
            _Http_configureRequest(xhr, request);
    
            request.body.a && xhr.setRequestHeader('Content-Type', request.body.a);
            xhr.send(request.body.b);
    

    If you open this file in the browser and step through this part of it in the debugger, you can see that Elm actually does put the given body "hi" in the request.body.b value, passed to xhr.send.

    But if you then look in the Network tab in the browser console, you can see that the request doesn't contain a body.

    So this is the browser stripping out the body with GET requests, not Elm.