问题
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.
回答1:
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
来源:https://stackoverflow.com/questions/38185875/how-to-enforce-json-encoding-for-phoenix-request