jerry
September 5, 2018, 3:07am
1
I am building a REST api using Elixir and Plug.
I am attempting to validate some variables and send specific error messages back to the client application.
In the process I get the error below:
07:56:36.926 [error] #PID<0.401.0> running Momo.Router (connection #PID<0.400.0>, stream id 1) terminated
Server: 5.153.40.138:7064 (http)
Request: GET /hello
** (exit) an exception was raised:
** (RuntimeError) expected dispatch/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection
(momo) lib/momo/router.ex:1: Momo.Router.plug_builder_call/2
(momo) lib/plug/debugger.ex:122: Momo.Router.call/2
(plug) lib/plug/adapters/cowboy2/handler.ex:12: Plug.Adapters.Cowboy2.Handler.init/2
(cowboy) /opt/apps/elixir_projects/momo/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
(cowboy) /opt/apps/elixir_projects/momo/deps/cowboy/src/cowboy_stream_h.erl:274: :cowboy_stream_h.execute/3
(cowboy) /opt/apps/elixir_projects/momo/deps/cowboy/src/cowboy_stream_h.erl:252: :cowboy_stream_h.request_process/3
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Please find my code below:
post "/process_request" do
{:ok, body, conn} = read_body(conn)
IO.puts body
body = Poison.decode!(body)
unless body |> Map.has_key?("amount") do
IO.puts "no amount"
send_resp(conn, 500, "Error")
halt(conn)
end
amount = body["amount"]
service_id = body["service_id"]
customer_number = body["customer_number"]
exttrid = body["exttrid"]
req_type = body["req_type"]
return_url = body["return_url"]
ts = body["ts"]
Operations.process_req(customer_number, exttrid, narration, amount, req_type, service_id, return_url)
##send_resp(conn, 201, "Request received")
end
Can anyone assist me?
Thanks.
idi527
September 5, 2018, 5:32am
2
Where do you expect the execution to stop in your code snippet?
jerry
September 5, 2018, 8:22am
3
Hi,
Sorry for responding late.
I want the execution to stop at the point I have halt(conn).
I do not want the remaining code to be executed.
Thanks.
NobbZ
September 5, 2018, 8:26am
4
You need to return the result of halt(conn)
.
Roughly like this:
post "/process_request" do
{:ok, body, conn} = read_body(conn)
IO.puts body
body = Poison.decode!(body)
if body |> Map.has_key?("amount") do
amount = body["amount"]
service_id = body["service_id"]
customer_number = body["customer_number"]
exttrid = body["exttrid"]
req_type = body["req_type"]
return_url = body["return_url"]
ts = body["ts"]
Operations.process_req(customer_number, exttrid, narration, amount, req_type, service_id, return_url)
##send_resp(conn, 201, "Request received")
else
IO.puts "no amount"
conn
|> send_resp(500, "Error")
|> halt()
end
end
Notice, that I use the pipe-operator (|>
) to pipe the conn
, as it wouldn’t store the 500 error in it otherwise. Elixir is immutable.
jerry
September 5, 2018, 8:35am
5
I thought about that solution but the issue is that I have to do validation for each of the parameters and stop execution if any of them fails.
Two things I have to check for each of the parameters listed in the route are:
checking to ensure that each of those keys is present in the request from the client
validating the content of the data in those parameters
Please advise me on the best way to go about this.
Thanks.
NobbZ
September 5, 2018, 8:43am
6
Then you should probably use the with/1
special-form.
post "/process_request" do
{:ok, body, conn} = read_body(conn) # Where comes this `conn` from?
with {:ok, parsed} <- Poison.decode(body)
{:ok, param1} <- validate_param1(parsed)
{:ok, param2} <- validate_param2(parsed)
do
process_request(param1, param2) # I hope this returns a `conn`!
else
{:error, reason} ->
conn
|> send_resp(500, "Error #{inspect reason}")
|> halt(conn)
end
end
1 Like
jerry
September 5, 2018, 8:51am
7
Great suggestion. Will try that out now.
I’m using Plugs.
Thanks.
jerry
September 8, 2018, 3:25am
8
Hello NobbZ,
Thanks a lot for your suggestion.
It worked exactly for me.
Thanks a lot.
jerry
September 8, 2018, 3:30am
9
final solution below:
{:ok, body, conn} = read_body(conn)
{:ok, parsed} = Poison.decode(body)
with {:ok, amount} <- parse_amount(parsed["amount"]),
{:ok, exttrid} <- parse_id(parsed["id"]),
{:ok, cust_no} <- parse_cust(parsed["cust_no"]),
{:ok, narration} <- parse_narration(parsed["narration"])
do
save(cust_no, id, amount, narrationo)
else
{:error, reason} ->
conn
|> send_resp(200, Poison.encode!(reason))
|> halt
end
1 Like