Phoenix构建RESTful API时出现函数子句匹配错误求助
Phoenix RESTful API 创建时POST请求返回400错误的问题
我正在学习用Phoenix创建RESTful API,操作步骤如下:
操作步骤
- 步骤1:使用mix命令生成数据库schema及相关代码
mix phx.gen.context AnimalAdmin Animal animal name:string email:string:unique role:string address:string mix phx.gen.json AnimalAdmin Animal animal name:string email:string:unique role:string address:string --no-context --no-schema
- 步骤2:执行数据库迁移
mix ecto.migrate
- 步骤3:更新router.ex文件
pipe_through :api get "/animal", AnimalController, :index put "/animal", AnimalController, :edit post "/animal", AnimalController, :create delete "/animal", AnimalController, :delete
- 步骤4:启动服务
mix phx.server
AnimalController代码
defmodule InvestmentBackWeb.AnimalController do use InvestmentBackWeb, :controller alias InvestmentBack.AnimalAdmin alias InvestmentBack.AnimalAdmin.Animal action_fallback InvestmentBackWeb.FallbackController def index(conn, _params) do animal = AnimalAdmin.list_animal() render(conn, "index.json", animal: animal) end def create(conn, %{"animal" => animal_params}) do with {:ok, %Animal{} = animal} <- AnimalAdmin.create_animal(animal_params) do conn |> put_status(:created) # |> put_resp_header("location", Routes.animal_path(conn, :show, animal)) |> render("show.json", animal: animal) end end def show(conn, %{"id" => id}) do animal = AnimalAdmin.get_animal!(id) render(conn, "show.json", animal: animal) end def update(conn, %{"id" => id, "animal" => animal_params}) do animal = AnimalAdmin.get_animal!(id) with {:ok, %Animal{} = animal} <- AnimalAdmin.update_animal(animal, animal_params) do render(conn, "show.json", animal: animal) end end def delete(conn, %{"id" => id}) do animal = AnimalAdmin.get_animal!(id) with {:ok, %Animal{}} <- AnimalAdmin.delete_animal(animal) do send_resp(conn, :no_content, "") end end end
错误信息
使用Postman发送POST请求添加数据时,返回400状态码,错误信息如下:
no function clause matching in InvestmentBackWeb.AnimalController.create/2 The following arguments were given to InvestmentBackWeb.AnimalController.create/2: # 1 %Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, body_params: %{"address" => "china", "email" => "smallstul@gmail.com", "name" => "xingxingliu", "role" => "new developer"}, cookies: %{}, halted: false, host: "localhost", method: "POST", owner: #PID<0.555.0>, params: %{"address" => "china", "email" => "smallstul@gmail.com", "name" => "xingxingliu", "role" => "new developer"}, path_info: ["animal"], path_params: %{}, port: 4000, private: %{InvestmentBackWeb.Router => {[], %{Plug.Swoosh.MailboxPreview => ["mailbox"]}}, :before_send => [#Function<0.11807388/1 in Plug.Telemetry.call/2>, #Function<1.86886788/1 in Phoenix.LiveReloader.before_send_inject_reloader/3>], :phoenix_action => :create, :phoenix_controller => InvestmentBackWeb.AnimalController, :phoenix_endpoint => InvestmentBackWeb.Endpoint, :phoenix_format => "json", :phoenix_layout => {InvestmentBackWeb.LayoutView, :app}, :phoenix_request_logger => {"request_logger", "request_logger"}, :phoenix_router => InvestmentBackWeb.Router, :phoenix_view => InvestmentBackWeb.AnimalView, :plug_session_fetch => #Function<1.84243074/1 in Plug.Session.fetch_session/1>}, query_params: %{}, query_string: "", remote_ip: {127, 0, 0, 1}, req_cookies: %{}, req_headers: [{"accept", "*/*"}, {"accept-encoding", "gzip, deflate, br"}, {"connection", "keep-alive"}, {"content-length", "118"}, {"content-type", "application/json"}, {"host", "localhost:4000"}, {"postman-token", "5528ed3e-d8de-4e2e-82e3-2d7b1b3bde39"}, {"user-agent", "PostmanRuntime/7.31.3"}], request_path: "/animal", resp_body: nil, resp_cookies: %{}, resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "F0-VSsiF-LxMK_gAAAAC"}], scheme: :http, script_name: [], secret_key_base: :..., state: :unset, status: nil} # 2 %{"address" => "china", "email" => "smallstul@gmail.com", "name" => "xingxingliu", "role" => "new developer"}
环境版本
Erlang/OTP 25 [erts-13.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [dtrace] Elixir 1.14.2 (compiled with Erlang/OTP 25)
Phoenix版本:1.6.16
问题原因及解决方案
问题核心是控制器的create函数参数匹配不匹配:
你的create函数定义为def create(conn, %{"animal" => animal_params}),期望参数包含外层的"animal"键,但Postman发送的请求是直接平铺的字段,没有嵌套在"animal"下。
有两种解决方式:
- 调整Postman请求格式:将请求体包裹在
animal键内,示例:
{ "animal": { "address": "china", "email": "smallstul@gmail.com", "name": "xingxingliu", "role": "new developer" } }
- 修改控制器代码:让
create函数直接接收平铺的参数:
def create(conn, animal_params) do with {:ok, %Animal{} = animal} <- AnimalAdmin.create_animal(animal_params) do conn |> put_status(:created) |> render("show.json", animal: animal) end end
注:phx.gen.context生成的create_animal函数默认支持直接接收平铺参数,无需额外修改上下文代码。
另外补充一个路由小问题:put "/animal", AnimalController, :edit不符合RESTful规范,应该改为put "/animal/:id", AnimalController, :update,因为更新资源需要指定ID,且对应控制器的update函数。
内容的提问来源于stack exchange,提问作者xingxing liu
相关产品推荐
相关产品推荐

