Простой Api, где создать функцию, отправьте мне 400 "Bad Request" в Фениксе - PullRequest
0 голосов
/ 06 ноября 2019

Этот пост идентичен этой проблеме: https://elixirforum.com/t/simple-api-where-create-function-send-me-400-bad-request-but-i-cant-figure-out-why/16456, но решение не работает для меня.

Вот мой контроллер:

defmodule ApaecWeb.UserController do
  use ApaecWeb, :controller

  alias Apaec.Auth
  alias Apaec.Auth.User

  action_fallback ApaecWeb.FallbackController

  def index(conn, _params) do
    IO.puts "user"
    users = Auth.list_users()
    render(conn, "index.json", users: users)
  end

  def create(conn, %{"user" => user_params}) do
    with {:ok, %User{} = user} <- Auth.create_user(user_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.user_path(conn, :show, user))
      |> render("show.json", user: user)
    end
  end

  def show(conn, %{"id" => id}) do
    user = Auth.get_user!(id)
    render(conn, "show.json", user: user)
  end

  def update(conn, %{"id" => id, "user" => user_params}) do
    user = Auth.get_user!(id)

    with {:ok, %User{} = user} <- Auth.update_user(user, user_params) do
      render(conn, "show.json", user: user)
    end
  end

  def delete(conn, %{"id" => id}) do
    user = Auth.get_user!(id)

    with {:ok, %User{}} <- Auth.delete_user(user) do
      send_resp(conn, :no_content, "")
    end
  end

  def sign_in(conn, %{"email" => email, "password" => password}) do
  case Apaec.Auth.authenticate_user(email, password) do
    {:ok, user} ->
      conn
      |> put_status(:ok)
      |> put_view(ApaecWeb.UserView)
      |> render("sign_in.json", user: user)

    {:error, message} ->
      conn
      |> put_status(:unauthorized)
      |> put_view(ApaecWeb.ErrorView)
      |> render("401.json", message: message)
    end
  end
end

Моя схема:

defmodule Apaec.Auth.User do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  schema "users" do
    field :email, :string, null: false
    field :is_active, :boolean, default: false, null: false
    field :password, :string, virtual: true
    field :password_hash, :string

    timestamps(type: :utc_datetime_usec)
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:email, :is_active, :password])
    |> validate_required([:email, :is_active, :password])
    |> unique_constraint(:email)
    |> put_pass_hash()
  end

  defp put_pass_hash(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
        put_change(changeset, :password_hash, Pbkdf2.hash_pwd_salt(pass))
      _ ->
        changeset
    end
  end
end

Мой FallbackController:

defmodule ApaecWeb.FallbackController do
  @moduledoc """
  Translates controller action results into valid `Plug.Conn` responses.

  See `Phoenix.Controller.action_fallback/1` for more details.
  """
  use ApaecWeb, :controller

  def call(conn, {:error, :not_found}) do
    conn
    |> put_status(:not_found)
    |> put_view(ApaecWeb.ErrorView)
    |> render(:"404")
  end

  def call(conn, {:error, %Ecto.Changeset{}}) do
    conn
    |> put_status(:unprocessable_entity)
    |> put_view(ApaecWeb.ErrorView)
    |> render(:"422")
  end

end

Мои маршруты:

defmodule ApaecWeb.Router do
  use ApaecWeb, :router

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/api", ApaecWeb do
    pipe_through :api

    resources "/users", UserController, except: [:new, :edit]
    post "/users/sign_in", UserController, :sign_in
  end
end

Когда я использую curl -H "Content-Type: application/json" -X POST http://localhost:4000/api/users -d {"user":{"email":"some@email.com","password":"some apssword"}} в терминале. Я продолжаю получать:

[info] POST /api/users
[info] Converted error Plug.Parsers.ParseError to 400 response

Я пытался отбросить часть user или использовать вместо нее users. Я не совсем понимаю решение, связанное с регистраторами, но проблема была не в этом.

РЕДАКТИРОВАТЬ: дополнительно запустив его на powershell, я получаю эту ошибку:

Invoke-WebRequest : Cannot bind parameter 'Headers'. Cannot convert the "Content-Type: application/json" value of type
"System.String" to type "System.Collections.IDictionary".
At line:1 char:9
+ curl -H "Content-Type: application/json" -X POST http://localhost:400 ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Invoke-WebRequest], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

, поэтому я попытался:

PS C:\Users\Me> $user = @{ email='dave@Dave.com'
>> password='pass'
>> }
PS C:\Users\Me> $json = $user | ConvertTo-Json
PS C:\Users\Me> Invoke-RestMethod 'http://localhost:4000/api/users' -Method POST -Body $json -ContentType 'application/json'
Invoke-RestMethod : {"errors":{"detail":"Bad Request"}}
At line:1 char:1
+ Invoke-RestMethod 'http://localhost:4000/api/users' -Method POST -Bod ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Это все еще дает плохой результат, однако естьнамного больше информации в консоли:

info] POST /api/users
[debug] Processing with ApaecWeb.UserController.create/2
  Parameters: %{"email" => "dave@Dave.com", "password" => "[FILTERED]"}
  Pipelines: [:api]
[info] Sent 400 in 0┬Ás
[info] Converted error Phoenix.ActionClauseError to 400 response
[info] POST /api/users
[info] Converted error Plug.Parsers.ParseError to 400 response

Ответы [ 2 ]

2 голосов
/ 06 ноября 2019

Я думаю, что ваши цитаты отключены

bash-3.2$ echo {"user":{"email":"some@email.com","password":"some apssword"}}
{user:email:some@email.com} {user:password:some apssword}

bash-3.2$ echo '{"user":{"email":"some@email.com","password":"some apssword"}}'
{"user":{"email":"some@email.com","password":"some apssword"}}
1 голос
/ 06 ноября 2019

Благодаря @Evadne, вы дали мне идею проверить терминал и синтаксис.

PS C:\Users\Me> $json = '{"user":{"email":"some@email.com","password":"some apssword"}}'
PS C:\Users\Me> Invoke-RestMethod 'http://localhost:4000/api/users' -Method POST -Body $json -ContentType 'application/json'

приводит к ожидаемому поведению. Не думайте, что что-то не так с API, просто, возможно, реализация curl в коде терминала Visual Studio.

...