Plug.Conn.resp/3 中没有匹配的函数子句

no function clause matching in Plug.Conn.resp/3

在使用 Guardian 验证针对 JWT 令牌的 GET 请求后,我正在尝试使用 JSON 进行响应。

我的测试:

  describe "Show user if authenticated" do
    setup %{conn: conn} do
      {:ok, %User{} = user} = Riders.create_user(@create_attrs)
      {:ok, jwt, claims} = Bikefit.Guardian.encode_and_sign(user)
      IO.inspect claims
      conn = put_req_header(conn, "authorization", "Bearer #{jwt}")
      {:ok, conn: conn, user: user}
    end

    test  "and return user if token is valid", %{conn: conn, user: user} do
      email = user.email
      conn = get conn, current_user_path(conn, :current)
      response = json_response(conn, 200)
      IO.puts "---------- test response--------"
      IO.inspect response
      assert %{"email" => email} = response
    end
  end

我的控制器:

defmodule BikefitWeb.UserController do
  use BikefitWeb, :controller

  alias Bikefit.Riders
  alias Bikefit.Riders.User

  plug Guardian.Plug.EnsureAuthenticated, handler: BikefitWeb.AuthController

  action_fallback BikefitWeb.FallbackController

  def current(conn, _params) do
    user = conn |> Guardian.Plug.current_resource

    conn
    |> render(BikefitWeb.UserView, "show.json-api", user: user)
  end
end

我的看法:

defmodule BikefitWeb.UserView do
  use BikefitWeb, :view
  alias BikefitWeb.UserView

  def render("show.json-api", %{user: user}) do
    %{data: render_one(user, UserView, "user.json")}
  end

  def render("user.json",  %{user: user}) do
    %{id: user.id,
      email: user.email,
      auth_provider: user.auth_provider}
  end
end

我的路由器:

defmodule BikefitWeb.Router do
  use BikefitWeb, :router

  pipeline :api do
    plug :accepts, ["json", "json-api"]
    plug JaSerializer.Deserializer
  end

  pipeline :api_auth do
    plug :accepts, ["json", "json-api"]
    plug Guardian.Plug.Pipeline, 
          module: Bikefit.Guardian, 
          error_handler: BikefitWeb.AuthErrorHandler
    plug Guardian.Plug.VerifyHeader, realm: "Bearer"
    plug Guardian.Plug.LoadResource
    plug JaSerializer.Deserializer
  end

  scope "/api/v1", BikefitWeb do
    pipe_through :api_auth

    resources "/users", UserController, except: [:new, :edit]
    get "/user/current", UserController, :current, as: :current_user
    delete "/logout", UserController, :delete
  end
end

测试的输出:

   ** (FunctionClauseError) no function clause matching in Plug.Conn.resp/3

     The following arguments were given to Plug.Conn.resp/3:

         # 1
         %Plug.Conn{adapter: {Plug.Adapters.Test.Conn, :...}, 
  assigns: %{layout: false, user: %Bikefit.Riders.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
  auth_provider: "some auth_provider", email: "some email", id: 272, inserted_at: ~N[2018-02-24 06:28:20.876426], 
  password: nil, password_hash: nil, updated_at: ~N[2018-02-24 06:28:20.876442]}}, 
  before_send: [#Function<1.42514850/1 in Plug.Logger.call/2>], body_params: %{}, 
  cookies: %Plug.Conn.Unfetched{aspect: :cookies}, halted: false, host: "www.example.com", method: "GET", owner: #PID<0.418.0>, 
  params: %{}, path_info: ["api", "v1", "user", "current"], path_params: %{}, peer: {{127, 0, 0, 1}, 111317}, 
  port: 80, private: %{BikefitWeb.Router => {[], %{}}, 
  :guardian_default_claims => %{"aud" => "Bikefit", "exp" => 1522045700, "iat" => 1519453700, "iss" => "Bikefit", "jti" => "5f8312a9-8f49-4f0a-9654-968882cd3f90", "nbf" => 1519453699, "sub" => "some email", "typ" => "access"}, 
  :guardian_default_resource => %Bikefit.Riders.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, auth_provider: "some auth_provider", email: "some email", id: 272, inserted_at: ~N[2018-02-24 06:28:20.876426], password: nil, password_hash: nil, updated_at: ~N[2018-02-24 06:28:20.876442]}, 
  :guardian_default_token => "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJCaWtlZml0IiwiZXhwIjoxNTIyMDQ1NzAwLCJpYXQiOjE1MTk0NTM3MDAsImlzcyI6IkJpa2VmaXQiLCJqdGkiOiI1ZjgzMTJhOS04ZjQ5LTRmMGEtOTY1NC05Njg4ODJjZDNmOTAiLCJuYmYiOjE1MTk0NTM2OTksInN1YiI6InNvbWUgZW1hac3MifQ.gt7B7itXs8HFWEzFwQxa5LJaDzSKkF1b2C4BDxw28nP3Q4_cHDi-PexZQDh8BbjD363qFKK9p9jvoQqqe9yx8A", :guardian_error_handler => BikefitWeb.AuthErrorHandler, 
 :guardian_module => Bikefit.Guardian, :phoenix_action => :current, 
 :phoenix_controller => BikefitWeb.UserController, :phoenix_endpoint => BikefitWeb.Endpoint, 
 :phoenix_format => "json", :phoenix_layout => {BikefitWeb.LayoutView, :app}, :phoenix_pipelines => [:api_auth], 
 :phoenix_recycled => true, :phoenix_router => BikefitWeb.Router, :phoenix_template => "show.json-api", 
 :phoenix_view => BikefitWeb.UserView, :plug_session_fetch => #Function<1.45862765/1 in Plug.Session.fetch_session/1>, 
 :plug_skip_csrf_protection => true}, query_params: %{}, query_string: "", remote_ip: {127, 0, 0, 1}, 
 req_cookies: %Plug.Conn.Unfetched{aspect: :cookies}, req_headers: [{"accept", "application/json"}, 
 {"authorization", "Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJCaWtlZml0IiwiZXhwIjoxNTIyMDQ1NzAwLCJpYXQiOjE1MTk0NTM3MDAsImlzcyI6IkJpa2VmaXQRmMGEtOTY1NC05Njg4ODJjZDNmOTAiLCJuYmYiOjE1MTk0NTM2OTksInN1YiI6InNvbWUgZW1haWwiLCJ0eXAiOiJhY2Nlc3MifQ.gt7B7itXs8HFWEzFwQxa5LJaDzSKkF1b2C4BDxw28nP3Q4_cHDi-PexZQDh8BbjD363qFKK9p9jvoQqqe9yx8A"}], 
 request_path: "/api/v1/user/current", resp_body: nil, resp_cookies: %{}, 
 resp_headers: [{"content-type", "application/vnd.api+json; charset=utf-8"}, 
 {"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "44e2jir0js16jn0pe2pf8qgbksc9346s"}], 
 scheme: :http, script_name: [], secret_key_base: "UD4qdbi6YOBRbCrf", 
 state: :unset, status: nil}

         # 2
         200

         # 3
         %{data: %{auth_provider: "some auth_provider", email: "some email", id: 272}}

     Attempted function clauses (showing 3 out of 3):

         def resp(%Plug.Conn{state: state}, status, _body) when not(state === :set or (state === :set_chunked or (state === :set_file or state === :unset)))
         def resp(%Plug.Conn{}, _status, nil)
         def resp(%Plug.Conn{} = conn, status, body) when is_binary(body) or is_list(body)

     code: conn = get conn, current_user_path(conn, :current)
     stacktrace:
       (plug) lib/plug/conn.ex:505: Plug.Conn.resp/3
       (plug) lib/plug/conn.ex:495: Plug.Conn.send_resp/3
       (bikefit) lib/bikefit_web/controllers/user_controller.ex:1: BikefitWeb.UserController.action/2
       (bikefit) lib/bikefit_web/controllers/user_controller.ex:1: BikefitWeb.UserController.phoenix_controller_pipeline/2
       (bikefit) lib/bikefit_web/endpoint.ex:1: BikefitWeb.Endpoint.instrument/4
       (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
       (bikefit) lib/bikefit_web/endpoint.ex:1: BikefitWeb.Endpoint.plug_builder_call/2
       (bikefit) lib/bikefit_web/endpoint.ex:1: BikefitWeb.Endpoint.call/2
       (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
       test/bikefit_web/controllers/user_controller_test.exs:63: (test)

我认为问题是 conn 的 state: :unset 导致函数子句匹配出现问题,但我不确定为什么会发生这种情况,因为我认为我的控制器和视图与教程匹配。有任何想法吗?

您需要通过 format_encoders configuration 告诉 Phoenix 如何编码不同的格式。默认情况下它知道如何编码 .json 但对 .json-api 一无所知,这就是为什么默认情况下不对它们进行编码的原因。

重命名它。json 或向格式编码器添加新条目应该足以修复它。

我在另一个问题上偶然发现了同样的错误:

我试图在我的控制器中呈现 xml 布局

用“put_root_layout(false)”去掉了它

  def general_feed_xml(conn, _params) do
    conn
    |> put_resp_content_type("text/xml")
    |> put_root_layout(false)
    |> render("general_feed.xml", [])
  end

如果对任何人有帮助。