How to get POST data with Plug

I feel really dumb asking this, but I’ve bopped all over the internet, tried various things, and checked out some posts on this and I’m perplexed at why I can’t figure out how to simply retrieve data passed to my program via POST. Here is my Router module

defmodule Kahwiz.Router do
  use Plug.Router

  plug(Plug.Parsers, parsers: [:urlencoded])

  plug(:match)
  plug(:dispatch)

  get "/" do
    Kahwiz.IndexController.index(conn)
  end

  post "/" do
    # IO.inspect(conn.params, label: "The params")
    {:ok, data, _conn} = read_body(conn)
    IO.inspect(data, label: "The data")
    send_resp(conn, 200, "Got it #{data}")
  end

  match _ do
    send_resp(conn, 404, "oops")
  end
end

And my CURL command to send data to the server:
curl -X POST http://localhost:4001?param1=1234

In its current condition, data is empty, but on the command line I do get “Got it”. If you use the IO.inspect line to get the parameters passed to it, I see the data in the inspect function but also a large amount of errors:

The params: %{"param1" => "1234"}

20:59:47.224 [error] #PID<0.892.0> running Kahwiz.Router (connection #PID<0.891.0>, stream id 1) terminated
Server: localhost:4001 (http)
Request: POST /?param1=1234
** (exit) an exception was raised:
    ** (Protocol.UndefinedError) protocol String.Chars not implemented for %{"param1" => "1234"} of type Map. This protocol is implemented for the following type(s): Decimal, NaiveDateTime, Atom, Date, BitString, Float, Integer, List, Time, Version.Requirement, Version, URI, DateTime
        (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
        (elixir) lib/string/chars.ex:22: String.Chars.to_string/1
        (kahwiz) lib/router.ex:20: anonymous fn/2 in Kahwiz.Router.do_match/4
        (kahwiz) lib/plug/router.ex:259: Kahwiz.Router.dispatch/2
        (kahwiz) lib/router.ex:1: Kahwiz.Router.plug_builder_call/2
        (plug_cowboy) lib/plug/cowboy/handler.ex:12: Plug.Cowboy.Handler.init/2
        (cowboy) /usr/home/courtney/Documents/code/Elixir/kahwiz/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) /usr/home/courtney/Documents/code/Elixir/kahwiz/deps/cowboy/src/cowboy_stream_h.erl:320: :cowboy_stream_h.execute/3

My goal in the end is to be able to submit the URL above and get “Got it 1234”

I’ve looked through the Plug docs on hexdocs but I’m having a hard time solving this. I’ve accomplished what I’m trying to do in the past with Phoenix but I’m trying to learn Elixir without a framework.

In your code you are trying to retrieve the body but you have not sent any body. The params are normal GET parameters which are retrieved another way. To post a body:

curl -X POST -d "param1=1234" http://localhost:4001

You can probably use Plug.Conn.fetch_query_params/2 to get the parameters from the query string rather than the body if that is what you are after.

You might also want to look into https://hexdocs.pm/plug/Plug.Parsers.html to read the parameters from the body in a more structured way.

2 Likes

You’re right. I’m dumb. I need to learn to IO.inspect more before I speak. I thought that conn.params contained the info initially and got nothing so I tried a variety things. It turned out to be the query_params as you suggest. In the end I changed to POST but I’m glad I learned a lot here and am remembering things. I’m picking up web development again after 3 years of stagnation and doing it with Elixir so I’m also drudging through remembering the intricacies of functional programming, since my only exposure to functional programming was using Racket 4 years ago :slight_smile:

2 Likes

Am asking what if I want to get some kind of Map so that I can get param details such as “username” and password in any http requests. So that I can use in backed.

The way am seeing at top, I get data as “bitsring” which I cannot use easily.

“name=John& password=opensesami” some string like that.

Looks like form encoded data, you should be able to use Plug.Conn.Query.decode/4 to decode it.