Эликсир UUID.Как обработать ошибку 500, когда UUID не совпадает - PullRequest
0 голосов
/ 16 декабря 2018
def show(conn, %{"id" => id}) do
  with {:ok, user} <- UserAction.get_user(id)
    |> put_status(200)
    |> render("show.json", %{user: user})
  else
    {:error, :not_found} -> {:error, :not_found, %User{id: id}}
  end
end

Когда идентификатор недействителен, Ecto повышает:

Ecto.Query.CastError - cannot be dumped to type :binary_id in query. 

Моя get_user функция:

query = from(u in User, where u.id == ^id)

case Repo.all(query) do
  [%User{} = user] -> {:ok, user}
  _ -> {:error, :not_found}
end

Есть ли какой-нибудь удобный способ обработки этой ошибки дляпредотвратить 500 ответов?

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

Это известная проблема с UUID, Binary и другими типами, которые должны соответствовать определенному стандарту ( Это функция, а не ошибка ™ ️ ).Как и в случае @ TheAnh , вы можете использовать Ecto.UUID.dump/1, чтобы проверить, действителен ли id, но я бы предпочел спасти его прямо:

def get_user(id) do
  Repo.get(User, id)
rescue
  Ecto.Query.CastError -> nil
end

Override Repo

Приведенный выше пример может быть утомительным, потому что вам придется rescue везде, где вы звоните get.Поэтому я переопределяю функцию get/3 в MyApp.Repo:

# lib/my_app/repo.ex
defoverridable [get: 2, get: 3]
def get(query, id, opts \\ []) do
  super(query, id, opts)
rescue
  Ecto.Query.CastError -> nil
end

Использование fetch для формата кортежа

Вы должны использовать fetch_* имена методов вместо get_* чтобы вернуть значения в формате tuple (чтобы избежать путаницы с методами по умолчанию Repo):

# lib/my_app/repo.ex
def fetch(query, id, opts \\ []) do
  case get(query, id, opts) do
    nil -> {:error, :not_found}
    schema -> {:ok, schema}
  end
end

И вызвать это так в вашей основной функции:

def fetch_user(id) do
  Repo.fetch(User, id)
end
0 голосов
/ 16 декабря 2018

Чтобы вернуть 400, вам нужно обновить conn с соответствующим статусом и затем выполнить рендеринг.

conn
|> put_status(:not_found)
|> put_view(YourApp.ErrorView)
|> render("404.html")

Это будет выполнено в предложении else вашего выражения with.Вы можете продолжить эту идею с пользовательскими ошибками: https://hexdocs.pm/phoenix/errors.html

Я бы посоветовал проверить, является ли ввод недействительным, прежде чем работать с ним, если это возможно.Есть несколько способов, которыми вы.может проверить перед попыткой выполнить запрос БД.Один надежный способ - попытаться привести значение перед попыткой запроса.

iex(1)> Ecto.UUID.cast("no good")
:error
iex(2)> Ecto.UUID.cast("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
:error
iex(3)> Ecto.UUID.cast("de851708-7f7a-40e1-b8ec-da2baec30839")
{:ok, "de851708-7f7a-40e1-b8ec-da2baec30839"}

Учитывая приведенное выше поведение, вы можете заключить ваше выражение with в выражение регистраХотя я не уверен, что и здесь я бы использовал with, лучше для конвейеров.

case Ecto.UUID.cast(id) do
  :error ->
    conn |> put_status(400) |> render("error.json", %{message: "Some Message"})

  {:ok, uuid} ->
    case UserAction.get_user(uuid) do
      {:ok, user} ->
        conn |> put_status(200) |> render("show.json", %{user: user})

      _ ->
        conn |> put_status(404) |> render("error.json", %{message: "User not found"})
    end
end
...