Ошибка при попытке отправить код состояния HTTP в Phoenix

Я новичок в Phoenix / Elixir и пытаюсь написать API, чтобы пользователи могли регистрироваться в моем приложении.

Конечная точка API работает должным образом, если я не пытаюсь установить код состояния HTTP для ответа. Когда я включаю строки 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

Мой код выглядит следующим образом:

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

Почему это происходит и где я ошибаюсь?


person Mohideen Imran Khan    schedule 16.09.2016    source источник


Ответы (1)


Изменить с 22 октября 2016 г. теперь это возможно на Plug master. Вот соответствующий раздел документации для справки:

Пользовательские коды статуса

Plug позволяет переопределить или добавить коды состояния, чтобы разрешить новые коды, не указанные напрямую Plug или его адаптерами. Добавление или изменение кода состояния выполняется через конфигурацию Mix приложения :plug. Например, чтобы переопределить существующую фразу причины 404 для кода состояния 404
(по умолчанию «Не найдено») и добавить новый код состояния 451, можно указать следующую конфигурацию:

  config :plug, :statuses, %{
    404 => "Actually This Was Found",
    451 => "Unavailable For Legal Reasons"
  }

Поскольку эта конфигурация специфична для Plug, Plug необходимо перекомпилировать, чтобы изменения вступили в силу: это не произойдет автоматически, поскольку зависимости не перекомпилируются автоматически при изменении их конфигурации. Чтобы перекомпилировать Plug:

MIX_ENV=prod mix deps.compile plug

Атомы, которые могут использоваться вместо кода состояния во многих функциях, зависят от фразы причины кода состояния. С приведенной выше конфигурацией все будет работать:

  put_status(conn, :not_found)                     # 404
  put_status(conn, :actually_this_was_found)       # 404
  put_status(conn, :unavailable_for_legal_reasons) # 451

Даже несмотря на то, что 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")

Не будет работать, так как plug разрешает только целое число или известный атом.

Вероятно, это следует считать ошибкой. Вы можете попытаться получить запрос на перенос в Cowboy из связанного мной файла.

Если объединить PR с Cowboy невозможно, это также можно выполнить в Plug для адаптера Cowboy, изменив статус (это наивное решение):

status = if (status == 451) do
  "451 Unavailable For Legal Reasons"
else
  status
end

В этом файле https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy/conn.ex#L33

См. Также https://github.com/ninenines/cowboy/issues/965 и https://github.com/elixir-lang/plug/issues/451

person Gazler    schedule 16.09.2016
comment
Спасибо за помощь и за то, что подняли вопрос по этому поводу. На данный момент я бы просто придерживался известных кодов HTTP и отправлял пользовательские коды как часть моего ответа JSON. - person Mohideen Imran Khan; 18.09.2016
comment
@MohideenImranKhan Я отправил для этого PR, и он был объединен в мастер, так что он будет доступен в следующем выпуске Plug. github.com/elixir-lang/plug/pull/470 - person Gazler; 22.10.2016