如何正确使用 `return` 进行 `render`:DoubleRenderError

How to properly use `return` for `render`: DoubleRenderError

我是 json 的新手,在返回时遇到了问题。我得到下面的错误,即使我到处都是 render,我也 return。是什么导致了这个错误?

AbstractController::DoubleRenderError in Api::V1::ArticlesController#show
Render and/or redirect were called multiple times in this action.

错误指的是下面def authenticate_user中的render json: {errors: "not authorized" }, status: :unauthorized。相反,我希望它只呈现 json 错误:"not authorized"。知道为什么这没有发生吗?

before_action :authenticate
def authenticate
  unless authenticated?
    render json: {errors: "not authorized" }, status: :unauthorized
    return
  end
end

这会调用以下辅助方法:

def authenticated?
  !current_user.nil?
end

def current_user
  token = request.headers["Authorization"]
  email = request.headers["HTTP_USER_EMAIL"]
  if token
    user = User.friendly.find_by(email: email)
    if user
      if user.token_expired?
        render json: {errors: "Session expired, please login" }, status: :unauthorized
        return
      elsif user.authenticated?(token)
        @current_user = user
      end
    end
  else
    nil
  end
end

更新: 为删除 return 提供的解决方案在任何地方都有效,但我不明白为什么。假设辅助方法如下所示。那么包含 return 就很重要了,对吧?因为否则,如果找不到@node,该方法仍将继续并且不会显示消息 "node not found" 。你能解释一下吗?

  def create
    @node = Node.find_by(id: params[:node_id])
    if @node.nil?
      render json: { errors: "node not found" }, status: :bad_request
      return
    end
    nodee = @node.create(create_params)
    if nodee.save
      render json: @node, status: :created
    else
      render json: @node, status: :bad_request
    end
  end

换句话说,我原以为从 def authenticate 中删除 render 会导致 Rails 继续使用 create 方法,因为 def authenticate 没有告诉它去那里(我是这样看的 render)。

Update2: 我也不能按照答案中的建议删除 render,而是将其移至行首:return render json: {errors: "not authorized" }, status: :unauthorized。很想知道这是为什么。

您的核心问题是 current_user 正在自己进行渲染 - 这是不寻常的,很容易意外渲染两次。第二件事是,如果在过滤器期间调用渲染或重定向,则前过滤器会停止处理操作:returning 从过滤器不会直接影响事物。

current_user 中删除 return 很大程度上是偶然的:这意味着 current_user 中的 return 值是渲染中的 return 值,即不再为零。这意味着 authenticated? return 为真(即使用户未通过身份验证)因此您的之前过滤器不会再次呈现。 Rails 然后停止执行操作,因为渲染是从过滤器中调用的,所以看起来一切正常。

一般来说,虽然您确实需要调用 return 来停止方法的其余部分执行,但这显然不会停止调用方法内部的进程。

就我个人而言,我会保留 authenticate 不变,但更改 current_user 以便它没有任何副作用(设置 @current_user 除外)。如果您真的想更改错误消息,请在单独的实例变量中跟踪 current_user 为 nil 的原因(实际上我可能不会滚动我自己的身份验证,但这是另一回事)

对于未来的旅行者:

在 rails API 控制器中使用保护子句和辅助方法以避免多行 if else 链时,请执行以下操作:

  1. 将辅助方法重命名为 ?方法
  2. Return如果helper_methods? return是真的
  3. 在辅助方法中,确保return如果满足条件则为真

干杯

def controller_method

return if invalid_email?(params["user"]["email"])
...
def invalid_email?(email)
  if EmailValidator.invalid?(email) then render(json: { status: 422, message: "Your email format is invalid. Please check that your email is correct and try again!" }, status: 422) and return true end
end