长生不老药 UUID。UUID不匹配时如何处理500错误

Vla*_*sky 5 postgresql uuid elixir phoenix-framework

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
Run Code Online (Sandbox Code Playgroud)

当 id 无效时,Ecto引发:

Ecto.Query.CastError - cannot be dumped to type :binary_id in query. 
Run Code Online (Sandbox Code Playgroud)

我的get_user功能:

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

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

是否有任何方便的方法来处理此错误以防止 500 个响应?

She*_*yar 6

这是UUIDBinary和其他需要符合特定标准的这是一个功能,而不是 bug\xe2\x84\xa2\xef\xb8\x8f)。就像@TheAnh提到的,你可以用来Ecto.UUID.dump/1检查是否id有效,但我更喜欢直接拯救它:

\n\n\n\n
def get_user(id) do\n  Repo.get(User, id)\nrescue\n  Ecto.Query.CastError -> nil\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

覆盖Repo

\n\n

上面的示例可能会变得乏味,因为您必须在rescue任何地方调用get。所以我重写了get/3以下函数MyApp.Repo

\n\n
# lib/my_app/repo.ex\ndefoverridable [get: 2, get: 3]\ndef get(query, id, opts \\\\ []) do\n  super(query, id, opts)\nrescue\n  Ecto.Query.CastError -> nil\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

用于fetch元组格式

\n\n

您应该使用fetch_*方法名称而不是get_*按格式返回值tuple(以避免与默认Repo方法混淆):

\n\n
# lib/my_app/repo.ex\ndef fetch(query, id, opts \\\\ []) do\n  case get(query, id, opts) do\n    nil -> {:error, :not_found}\n    schema -> {:ok, schema}\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

并在你的 main 函数中这样调用它:

\n\n
def fetch_user(id) do\n  Repo.fetch(User, id)\nend\n
Run Code Online (Sandbox Code Playgroud)\n


Vla*_*sky 3

我最终得到了守卫宏

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
Run Code Online (Sandbox Code Playgroud)

用法:

def get_user(id) when is_uuid(id) do
  Repo.get(User, id)
end

def get_user(_id), do: {:error, :not_found}
Run Code Online (Sandbox Code Playgroud)