I'm getting this error when a request hits my router at OPTIONS "/products"
error
14:45:33.433 [error] #PID<0.339.0> running Api.Router terminated
Server: 192.168.20.3:4000 (http)
Request: OPTIONS /products
** (exit) an exception was raised:
** (Poison.EncodeError) unable to encode value: {:brand, {"can't be blank", [validation: :required]}}
(poison) lib/poison/encoder.ex:383: Poison.Encoder.Any.encode/2
(poison) lib/poison/encoder.ex:259: anonymous fn/3 in Poison.Encoder.List.encode/3
(poison) lib/poison/encoder.ex:260: Poison.Encoder.List."-encode/3-lists^foldr/2-1-"/3
(poison) lib/poison/encoder.ex:260: Poison.Encoder.List.encode/3
(poison) lib/poison/encoder.ex:227: anonymous fn/4 in Poison.Encoder.Map.encode/3
(poison) lib/poison/encoder.ex:228: Poison.Encoder.Map."-encode/3-lists^foldl/2-0-"/3
(poison) lib/poison/encoder.ex:228: Poison.Encoder.Map.encode/3
(poison) lib/poison/encoder.ex:227: anonymous fn/4 in Poison.Encoder.Map.encode/3
Image showing the error caught on the frontend that occurred during the network request:
Judging by the image above it looks like the product JSON object does get sent so for some reason it isn't correctly mapping the http post body to the elixir changeset.
log of conn:
Interactive Elixir (1.4.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> %{}
%Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...}, assigns: %{},
before_send: [], body_params: %{},
cookies: %Plug.Conn.Unfetched{aspect: :cookies}, halted: false,
host: "192.168.20.3", method: "OPTIONS", owner: #PID<0.339.0>, params: %{},
path_info: ["products"], path_params: %{}, peer: {{192, 168, 20, 3}, 63793},
port: 4000,
private: %{plug_route: #Function<1.14347947/1 in Api.Router.do_match/4>},
query_params: %{}, query_string: "", remote_ip: {192, 168, 20, 3},
req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},
req_headers: [{"host", "192.168.20.3:4000"}, {"connection", "keep-alive"},
{"access-control-request-method", "POST"}, {"origin", "http://evil.com/"},
{"user-agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/
537.36"},
{"access-control-request-headers", "content-type,x-requested-with"},
{"accept", "*/*"}, {"referer", "http://localhost:8081/debugger-ui"},
{"accept-encoding", "gzip, deflate, sdch"},
{"accept-language", "en-GB,en-US;q=0.8,en;q=0.6"}], request_path: "/products",
resp_body: nil, resp_cookies: %{},
resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}],
scheme: :http, script_name: [], secret_key_base: nil, state: :unset,
status: nil}
%Api.Product{__meta__: #Ecto.Schema.Metadata<:built, "products">, brand: nil,
description: nil, id: nil, image: nil, name: nil, numberOfVotes: nil,
rating: nil}
router.ex
defmodule Api.Router do
use Plug.Router
if Mix.env == :dev do
use Plug.Debugger
end
plug :match
plug Plug.Parsers, parsers: [:json],
pass: ["application/json"],
json_decoder: Poison
plug :dispatch
get "/favicon.ico" do
# Api.Repo.getCategories(conn)
end
get "/categories/" do
Api.Repo.getCategories(conn)
end
options "/categories/" do
Api.Repo.getCategories(conn)
end
post "/products" do
Api.Repo.insertProduct(conn, conn.body_params)
end
options "/products" do
IO.puts inspect conn.body_params
Api.Repo.insertProduct(conn, conn.body_params)
end
get "/products" do
Api.Repo.insertProduct(conn, conn.body_params)
end
end
In repo.ex
def insertProduct(conn, product) do
IO.inspect(conn)
changeset = Api.Product.changeset(%Api.Product{}, product)
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: "success"
}))
{:error, changeset} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(500, Poison.encode!(%{
failure: changeset
}))
end
end
product.ex
defmodule Api.Product do
use Ecto.Schema
@derive {Poison.Encoder, only: [:name, :brand, :description, :image, :rating, :numberOfVotes]}
schema "products" do
field :name, :string
field :brand, :string
field :description, :string
field :image, :string
field :rating, :integer
field :numberOfVotes, :integer
end
def changeset(product, params \\ %{}) do
product
|> Ecto.Changeset.cast(params, [:name, :brand, :description, :image, :rating, :numberOfVotes])
|> Ecto.Changeset.validate_required([:name, :description, :brand])
end
end
By the way - origin is evil.com because of a browser plugin I use to enable CORS