如何正确使用`return`作为`render`:DoubleRenderError

时间:2016-01-24 14:28:52

标签: ruby-on-rails ruby json ruby-on-rails-4 rendering

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

  

Abstract Atroller :: Api中的DoubleRenderError :: V1 :: ArticlesController#show
  在此操作中多次调用渲染和/或重定向。

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

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,该方法仍将继续,并且找不到消息"节点"不会显示。你能解释一下吗?

  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

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

更新2:我也无法按照答案中的建议删除render,而是将其移至该行的开头:return render json: {errors: "not authorized" }, status: :unauthorized。很想明白为什么会这样。

2 个答案:

答案 0 :(得分:1)

你的核心问题是current_user正在进行渲染本身 - 这很不寻常,因此很容易意外渲染两次。第二件事是,如果在过滤器期间调用渲染或重定向,则前过滤器会停止对动作的处理:从过滤器返回并不会直接影响事物。

current_user中删除返回很大程度上是偶然的:这意味着current_user的返回值是render的返回值,即不再为nil。这意味着authenticated?返回true(即使用户未经过身份验证),因此您之前的过滤器不会再次呈现。然后Rails停止执行该操作,因为从过滤器中调用了渲染,因此看起来事情已经起作用。

一般情况下,你确实需要调用return来停止执行方法的其余部分,但是这显然不会在调用方法中停止进程。

就个人而言,我会按原样保留身份验证,但更改current_user,以便它不会产生任何副作用(除了设置@current_user)。如果你真的想要更改错误消息,那么在一个单独的实例变量中跟踪current_user为零的原因(事实上我可能不会滚动我自己的身份验证,但那是另一个故事)

答案 1 :(得分:0)

对于未来的旅行者:

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

  1. 将辅助方法重命名为 ?方法
  2. 如果 helper_methods 返回?返回真
  3. 在helper 方法中,如果满足条件,请确保返回true

干杯

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