尝试在Phoenix中发送HTTP状态代码时出错

Moh*_*han 3 elixir cowboy phoenix-framework

我是Phoenix/Elixir的初学者,我正在尝试编写API以允许用户在我的应用程序中注册.

除非我尝试设置响应的HTTP状态代码,否则API端点将按预期工作.当我包括A,B和C行(在下面的代码中指出)时,我得到了一条FunctionClauseError消息no function clause matching in :cowboy_req.status/1.

完整的错误消息如下:

[error] #PID<0.344.0> running App.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /api/user/
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in :cowboy_req.status/1
        (cowboy) src/cowboy_req.erl:1272: :cowboy_req.status(451)
        (cowboy) src/cowboy_req.erl:1202: :cowboy_req.response/6
        (cowboy) src/cowboy_req.erl:933: :cowboy_req.reply_no_compress/8
        (cowboy) src/cowboy_req.erl:888: :cowboy_req.reply/4
        (plug) lib/plug/adapters/cowboy/conn.ex:34: Plug.Adapters.Cowboy.Conn.send_resp/4
        (plug) lib/plug/conn.ex:356: Plug.Conn.send_resp/1
        (app) web/controllers/user_controller.ex:1: App.UserController.action/2
        (app) web/controllers/user_controller.ex:1: App.UserController.phoenix_controller_app/2
        (app) lib/app/endpoint.ex:1: App.Endpoint.instrument/4
        (app) lib/phoenix/router.ex:261: App.Router.dispatch/2
        (app) web/router.ex:1: App.Router.do_call/2
        (app) lib/app/endpoint.ex:1: App.Endpoint.phoenix_app/1
        (app) lib/plug/debugger.ex:122: App.Endpoint."call (overridable 3)"/2
        (app) lib/app/endpoint.ex:1: App.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
Run Code Online (Sandbox Code Playgroud)

我的代码如下:

defmodule App.UserController do
  use App.Web, :controller

  import Ecto.Changeset

  alias App.User
  alias App.Session

  def create(conn, params) do
    changeset = User.changeset(%User{}, params)

    case Repo.insert(changeset) do
      {:ok, _user} ->
        email = get_field(changeset, :email)
        password = get_field(changeset, :password)

        # Log on user upon sign up
        session_changeset = Session.changeset(%Session{
          email: email,
          password: password
        })
        result = Repo.insert(session_changeset)

        case result do
          {:ok, session} ->
            conn
            |> put_resp_cookie("SID", session.session_id)
            |> put_status(201)  # line A
            |> render("signup.json", data: %{
                 changeset: changeset
               })
          {:error, changeset} ->
            conn
            |> put_status(251)  # line B
            |> render("signup.json", data: %{
                 changeset: changeset
               })
        end
      {:error, changeset} ->
        conn
        |> put_status(451)  # line C
        |> render("signup.json", data: %{
             changeset: changeset
           })
    end
  end

end
Run Code Online (Sandbox Code Playgroud)

为什么会这样,我哪里出错?

Gaz*_*ler 7

编辑截至2016年10月22日,现在可以在Plug master上进行.以下是文档的相关部分供参考:

自定义状态代码

插件允许覆盖或添加状态代码,以允许新的代码不是由Plug或其适配器直接指定的.添加或覆盖状态代码是通过:plug应用程序的Mix配置完成的.例如,要覆盖404状态代码的现有404原因短语
(默认情况下为"未找到")并添加新的451状态代码,可以指定以下配置:

  config :plug, :statuses, %{
    404 => "Actually This Was Found",
    451 => "Unavailable For Legal Reasons"
  }
Run Code Online (Sandbox Code Playgroud)

由于此配置是特定于插件的,因此需要重新编译插件才能进行更改:这不会自动发生,因为在配置更改时不会自动重新编译依赖项.要重新编译插件:

MIX_ENV=prod mix deps.compile plug
Run Code Online (Sandbox Code Playgroud)

在许多函数中可用于代替状态代码的原子是从状态代码的原因短语中变换的.使用上面的配置,以下将全部工作:

  put_status(conn, :not_found)                     # 404
  put_status(conn, :actually_this_was_found)       # 404
  put_status(conn, :unavailable_for_legal_reasons) # 451
Run Code Online (Sandbox Code Playgroud)

尽管已经覆盖了404,但:not_found仍然可以使用原子将状态设置为404,以及:actually_this_was_found 从原因短语"实际上找到它"中选择的新原子 .


牛仔手动指定HTTP响应代码并匹配指定的整数.

https://github.com/ninenines/cowboy/blob/1.0.x/src/cowboy_req.erl#L1318

允许使用二进制文件,但是执行:

conn
|> put_status("451 Unavailable For Legal Reasons")
Run Code Online (Sandbox Code Playgroud)

插件只允许整数或已知原子,不起作用.

这应该被视为一个错误.您可以尝试在我链接的文件中向Cowboy获取拉取请求.

如果将PR合并到牛仔是不可能的,它也可以通过转换状态在Plug for the Cowboy适配器中执行(这是一个天真的解决方案):

status = if (status == 451) do
  "451 Unavailable For Legal Reasons"
else
  status
end
Run Code Online (Sandbox Code Playgroud)

在此文件中https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy/conn.ex#L33

另见https://github.com/ninenines/cowboy/issues/965https://github.com/elixir-lang/plug/issues/451