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
如果对任何人有帮助。
在使用 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
如果对任何人有帮助。