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
当id无效时,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次响应?
答案 0 :(得分:1)
这是UUID
,Binary
和其他ecto类型的需要符合特定标准的已知问题(这是一个功能,不是bug™️)。就像提到的 @TheAnh 一样,您可以使用Ecto.UUID.dump/1
来检查id
是否有效,但我更希望直接对其进行挽救:
def get_user(id) do
Repo.get(User, id)
rescue
Ecto.Query.CastError -> nil
end
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
答案 1 :(得分:0)
要返回400,您需要将conn
更新为适当的状态,然后进行渲染。
conn
|> put_status(:not_found)
|> put_view(YourApp.ErrorView)
|> render("404.html")
这将在您的with
表达式的else子句中执行。您可以通过自定义错误进一步了解这一想法: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
表达式包装在case表达式中。不过,我不确定我是否也可以在这里使用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
答案 2 :(得分:0)
我已经有了保护宏
defmacro is_uuid(value) do
quote do
is_binary(unquote(value)) and byte_size(unquote(value)) == 36 and
binary_part(unquote(value), 8, 1) == "-" and binary_part(unquote(value), 13, 1) == "-" and
binary_part(unquote(value), 18, 1) == "-" and binary_part(unquote(value), 23, 1) == "-"
end
end
用法:
def get_user(id) when is_uuid(id) do
Repo.get(User, id)
end
def get_user(_id), do: {:error, :not_found}
答案 3 :(得分:0)
感谢@vlad-horbachevsky 的回答,我将其扩展到我的守卫功能版本:
defguard is_uuid(value)
when is_binary(value) and byte_size(value) == 36 and
binary_part(value, 1, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 2, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 3, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 4, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 5, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 6, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 7, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 8, 1) == "-" and
binary_part(value, 9, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 10, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 11, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 12, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 13, 1) == "-" and
binary_part(value, 14, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 15, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 16, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 17, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 18, 1) == "-" and
binary_part(value, 19, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 20, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 21, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 22, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 23, 1) == "-" and
binary_part(value, 24, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 25, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 26, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 27, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 28, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 29, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 30, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 31, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 32, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 33, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 34, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F| and
binary_part(value, 35, 1) in ~w|0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F|
```