【问题标题】:Phoenix, Does conn redirect stop all further processing?凤凰城,conn 重定向是否会停止所有进一步的处理?
【发布时间】:2016-12-07 11:46:04
【问题描述】:

我有一个控制器,我想将 facebook 页面 ID 和访问令牌添加到我的数据库中,以便我可以代表用户发布。

为此,我需要对 facebook 进行 GET 调用,然后将访问令牌插入到我的数据库中。

这意味着我有一个嵌套的 case 语句,因为有 2 点错误,来自 facebook 调用和插入 repo:

case response_from_facebook do
   {:ok, token} ->
       case Repo.insert(token) do
            {:ok, _} -> ... redirect the user
            {:error, _} -> ... show an error message
   {:error, _} -> redirect and show an error

这是丑陋的 imo,所以我想知道如果它会停止进一步处理,我是否做了类似守卫的事情:

token = case response_from_facebook do
    {:ok, token} -> token
    {error,_ } -> conn |> redirect(to: user_path(conn, :show, user_id))
end

# Does it ever try to insert into the repo if an error occurs?

Repo.update ... etc.

这将使我的代码更易于理解/阅读,但是,我不完全确定重定向是否会阻止尝试插入我的存储库。如果这个“守卫”不是解决方案,我该如何防止多嵌套的 case 语句难以阅读?

【问题讨论】:

  • response_from_facebook 是否返回与Repo.insert 错误分开的易于模式匹配的错误(您能发布它可以返回的错误类型吗?)?如果是,您可以使用新的with 宏来简化这段代码。
  • @Dogbert 你能提供一个答案/例子吗?我在网上查看了 with 宏,但似乎没有一个示例真正显示了错误是如何处理的。您是否应该将with 的结果分配给某些东西?来自 facebook 的错误格式为 {:ok, %{"error" => %{"message" => message }}},而来自 ecto 的错误格式为 {:error, reason}。因此它们可以进行模式匹配。但是我不完全确定这应该如何发生。 (我应该在with 语句中分支错误吗?我应该分配with 语句的结果以供进一步处理吗?)
  • with 内查看带有else 块的示例:elixir-lang.org/docs/stable/elixir/…

标签: nested elixir phoenix-framework


【解决方案1】:

一种更简洁的方法:

defmodule SomeApp.FacebookController do
  ...

  def create(conn, params) do
    get_from_facebook
    |> process_facebook(conn)
  end

  defp get_from_facebook do
    # this is a function you implement
  end

  defp process_facebook({:ok, token}, conn) do
    Repo.insert(token)
    |> process_insert(conn)
  end

  defp process_facebook({:error, error}, conn) do
    conn
    |> redirect_with_error(error) # this is a function you will write
  end

  defp process_insert({:ok, _}, conn) do
    conn 
    |> redirect(to: user_path(conn, :show, user_id))
  end

  defp process_insert({:error, error}, conn) do
    conn
    |> redirect_with_error(error) # this is a function you will write
  end

  ...
end

这种逻辑分解不会产生不良影响,而且“更漂亮”。

如果您将大部分逻辑分解到另一个模块中,那就更好了:

defmodule SomeApp.FacebookCommand do

  def call do
    make_request
    |> process_response
  end

  defp make_request do
    # whatever you are doing to request from Facebook
  end

  defp process_response({:ok, token}) do
    Repo.insert(token)
    |> process_insert(conn)
  end

  defp process_response({:error, error}) do
    {:error, error}
  end

  defp process_insert({:ok, record}) do
    {:ok, record}
  end

  defp process_insert({:error, error}) do
    {:error, error}
  end

end

然后在控制器中使用:

defmodule SomeApp.FcebookController do
  ...

  def create(conn, params) do
    case SomeApp.FacebookCommand.call do
      {:ok, user} ->
        conn
        |> redirect(to: user_path(conn, :show, user.id))
      {:error, error} ->
        # redirect with error
    end
  end

  ...
end

显然,如果您要更新现有用户,那么您将编写命令,将现有用户作为参数传递给 call。'

请记住我的模块和函数命名很糟糕,因为我不知道你在做什么。更好的命名是必须的。

【讨论】:

  • 这可能只是风格问题,但在没有多个管道时尽量避免使用|>redirect_with_error(conn, error) 优于 conn |> redirect_with_error(error)。此外,当您使用管道时,您应该使用一个值来启动集合。 token |> Repo.insert() |> process_insert(conn) 会比 Repo.insert(token) |> process_insert(conn) 更好
  • 我基本同意你的看法。但是,我喜欢使用管道运算符来显示高级工作流,这就是我在上面只有 1 个参数的情况下使用它的原因。也就是说,其他一些风格问题是由于复制和粘贴上面提出的特伦斯的原始想法,即。 Repo.insert(token)。感谢您的意见。
猜你喜欢
  • 2016-05-03
  • 1970-01-01
  • 2017-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-16
  • 2015-09-06
  • 1970-01-01
相关资源
最近更新 更多