You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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&lt;0.555.0&gt;, 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&lt;0.11807388/1 in Plug.Telemetry.call/2&gt;, #Function&lt;1.86886788/1 in Phoenix.LiveReloader.before_send_inject_reloader/3&gt;], :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&lt;1.84243074/1 in Plug.Session.fetch_session/1&gt;}, 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"下。

有两种解决方式:

  1. 调整Postman请求格式:将请求体包裹在animal键内,示例:
{
  "animal": {
    "address": "china",
    "email": "smallstul@gmail.com",
    "name": "xingxingliu",
    "role": "new developer"
  }
}
  1. 修改控制器代码:让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 17:12:55