Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elm http request returns NetworkError for successful requests

Tags:

http

elm

I have a fair amount of experience building web apps with React but want to learn Elm. I've been banging my head against an HTTP request issue for a couple days now.

I'm running an Elm app at localhost:8080 and my supporting API at localhost:8081. Whenever I make an HTTP request (I've tried both GET and POST requests) I get a NetworkError. I've been looking into Elm JSON decoders and think it's possible that this is where my problem exists but I've tried sending simple strings from my server and using the Decode.string decoder in my Elm app and I still get the NetworkError.

Here's what my code looks like currently:

Commands.elm

module Commands exposing (..)

import Models exposing (..)
import Msg exposing (..)
import Http
import Json.Decode as Decode
import Json.Encode as Encode


createTempUser : Model -> Cmd Msg
createTempUser model =
  let
    tempUserBody =
      [ ( "firstname", Encode.string model.firstname )
      , ( "lastname", Encode.string model.lastname )
      , ( "phone", Encode.string model.phone )
      ]
        |> Encode.object
        |> Http.jsonBody

    url =
      myAPIUrl ++ "/endpoint"

    contentType = Http.header "Content-type" "text/plain"

    post =
      Http.request
        { method = "POST"
        , headers =  [contentType]
        , url = url
        , body = tempUserBody
        , expect = Http.expectJson decodeApiResponse
        , timeout = Nothing
        , withCredentials = False
        }

  in
    Http.send Msg.TempUserCreated post


decodeApiResponse : Decode.Decoder ApiResponse
decodeApiResponse =
  Decode.map4 ApiResponse
    (Decode.at ["status"] Decode.int)
    (Decode.at ["message"] Decode.string)
    (Decode.at ["created"] Decode.int)
    (Decode.at ["error"] Decode.string)


myAPIUrl : String
myAPIUrl = "http://localhost:8081"

Models.elm

module Models exposing (..)


type alias ApiResponse =
  { status: Int
  , message: String
  , created: Int
  , error: String
  }

Msg.elm

module Msg exposing (..)

import Navigation exposing (Location)
import Models exposing (..)
import Http


type Msg
  = ChangeLocation String
  | OnLocationChange Location
  | CreateTemporaryUser
  | TempUserCreated ( Result Http.Error ApiResponse )

Update.elm

module Update exposing (..)

import Msg exposing (Msg)
import Models exposing (..)
import Routing exposing (..)
import Commands exposing (..)
import Navigation

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Msg.ChangeLocation path ->
      ( { model | changes = model.changes + 1 }, Navigation.newUrl path )

    Msg.OnLocationChange location ->
      ( { model | error = "", route = parseLocation(location) }, Cmd.none )

    Msg.CreateTemporaryUser ->
      ( model, createTempUser model )

    Msg.TempUserCreated (Ok res) ->
       update (Msg.ChangeLocation signupCodePath) { model | httpResponse = toString(res) }

    Msg.TempUserCreated (Err err) ->
      ( { model | error = toString(err) }, Cmd.none )

Chrome's Network devtools show the response as this

{"status":200,"message":"Successfully inserted temporary 
user","created":1518739596447,"error":""}

I think this may be all the relevant code but if there's more you need to see I'll make an update including the requested code. I'll admit that I don't have a full understanding of the Elm Json.Decode library but I was under the impression that if this was where the issue was I would get an UnexpectedPayload Error that included additional context.

like image 801
Michael Fox Avatar asked Feb 16 '18 00:02

Michael Fox


2 Answers

@Sidney and @SimonH,

Thank you for looking into this for me, I feel bad because this wasn't an Elm issue after all.

The solution ended up using a CORS middleware on the server to add the 'Access-Control-Allow-Origin' header. The server was still responding with a 200 status and even included the entire successful response body but this resulted in a failure in Elm's Http Result.

Thank you again for your time and thought!

like image 90
Michael Fox Avatar answered Nov 20 '22 17:11

Michael Fox


For a quick solution while developing you can use the CORS everywhere extension for firefox and add the following to your whitelist in the extension preferences:

/^https?...localhost:8888\//i

For chrome you can probably use this extension

like image 45
Anton Avatar answered Nov 20 '22 18:11

Anton