Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enforce JSON encoding for Phoenix Request?

There's some API made with Phoenix, the API works with JSON.

But, when you test it and sent JSON with curl it fail because Phoenix doesn't parse the request as JSON. You need to explicitly add application/json header to curl. I'd like to make it more robust and tell Phoenix to always parse all requests as JSON.

Is there a way to force Phoenix to always treat requests as JSON and parse it as JSON?

UPDATE

I tried to use plug to set request headers as @AbM suggested, with the following code in Router:

def set_format conn, format do Plug.Conn.put_private conn, :phoenix_format, format end

def put_req_header conn, {key, value} do Plug.Conn.put_req_header conn, key, value end

pipeline :api do
  plug :put_req_header, {"accept", "application/json"}
  plug :put_req_header, {"content-type", "application/json"}
  plug :set_format, "json"
  plug :accepts, ["json"]
end

The request has been made with CURL

curl -X POST http://localhost:4000/api/upload -d '{"key": "value"}'                    

The connection looks like:

%Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...}, assigns: %{},
 before_send: [#Function<1.93474994/1 in Plug.Logger.call/2>,
  #Function<0.119481890/1 in Phoenix.LiveReloader.before_send_inject_reloader/1>],
 body_params: %{"{\"key\": \"value\"}" => nil},
 cookies: %Plug.Conn.Unfetched{aspect: :cookies}, halted: false,
 host: "localhost", method: "POST", owner: #PID<0.483.0>,
 params: %{"{\"key\": \"value\"}" => nil},
 path_info: ["api", "upload"], peer: {{127, 0, 0, 1}, 58408},
 port: 4000,
 private: %{App.Router => {[], %{}},
   :phoenix_action => :upload,
   :phoenix_controller => ApiController, :phoenix_endpoint => App.Endpoint,
   :phoenix_format => "json", :phoenix_layout => {LayoutView, :app},
   :phoenix_pipelines => [:api],
   :phoenix_route => #Function<8.59735990/1 in App.Router.match_route/4>,
   :phoenix_router => App.Router, :phoenix_view => ApiView,
   :plug_session_fetch => #Function<1.89562996/1 in Plug.Session.fetch_session/1>},
 query_params: %{},
 query_string: "", remote_ip: {127, 0, 0, 1},
 req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},
 req_headers: [{"user-agent", "curl/7.37.1"}, {"host", "localhost:4000"},
  {"accept", "application/json"}, {"content-length", "16"},
  {"content-type", "application/json"}],
 request_path: "/api/upload", resp_body: nil, resp_cookies: %{},
 resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"},
  {"x-request-id", "xxx"}], scheme: :http,
 script_name: [],
 secret_key_base: "xxx",
 state: :unset, status: nil}

It works if I add the -H "Content-Type: application/json" parameter to CURL, without it it doesn't work.

like image 666
Alex Craft Avatar asked Jul 04 '16 13:07

Alex Craft


1 Answers

If somebody will google for that and want to implement such behaviour.

lib/%APP_NAME%/endpoint.ex

 plug Plug.Head

 # add custom plug 
 plug :set_format, "json"

Define it:

  defp set_format(conn, format) do
    if Regex.match?(~r/^api.*/, conn.host) do
      Plug.Conn.put_private conn, :phoenix_format, format
    else
      conn
    end
  end

In this example we have dirty hack which will enforce JSON format for subdomain api

It's not recommended to do like that but since Phoenix enforcing HTML at any time this hack fix ridiculous behaviour like: Elixir.Phoenix.Router.NoRouteError for pipeline :api to show 404.json instead of 404.html

like image 94
Noma4i Avatar answered Nov 09 '22 16:11

Noma4i