如何为Phoenix Request强制执行JSON编码?

时间:2016-07-04 13:23:48

标签: elixir phoenix-framework

使用Phoenix制作了一些API,API与JSON一起使用。

但是,当您测试它并使用curl发送JSON时,它会失败,因为Phoenix不会将请求解析为JSON。您需要向application/json明确添加curl标头。我想让它更强大,并告诉Phoenix总是将所有请求解析为JSON。

有没有办法强制Phoenix始终将请求视为JSON并将其解析为JSON?

更新

我尝试使用插件来设置@AbM建议的请求标头,并在路由器中使用以下代码:

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

请求已使用CURL

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

连接如下:

%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}

如果我将-H "Content-Type: application/json"参数添加到CURL,它可以正常工作,没有它它不起作用。

2 个答案:

答案 0 :(得分:1)

如果有人会谷歌并希望实施此类行为。

lib/%APP_NAME%/endpoint.ex

 plug Plug.Head

 # add custom plug 
 plug :set_format, "json"

定义它:

  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

在这个例子中,我们有一个脏黑客,它将强制子域{J}格式为api

不建议这样做,但由于Phoenix在任何时候强制执行此黑客修复了荒谬的行为,例如:pipeline :api的Elixir.Phoenix.Router.NoRouteError显示404.json而不是404.html

答案 1 :(得分:0)

这个为我工作:

更改lib/%APP_NAME%/endpoint.ex

...

plug Plug.RequestId
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]

plug :set_header, {"accept", "application/json"}
plug :set_header, {"content-type", "application/json"}

...

plug Plug.Parsers,
  parsers: [:urlencoded, :multipart, :json],
  pass: ["*/*"],
  json_decoder: Phoenix.json_library()

...

plug ExColeWeb.Router

defp set_header(conn, {header, value}) do
  Plug.Conn.put_req_header(conn, header, value)
end