将 "render" 键从 "plain" 更改为 "json" 会导致服务器错误

Changing "render" key from "plain" to "json" causes server error

考虑以下代码

class AuthenticatedController < ApplicationController
  rescue_from InvalidCredentials, with: :unauthenticated

  def current_user
    raise InvalidCredentials
  end

  private

  def unauthenticated(_error)
    render plain: { error: 'text' }.to_json, status: :unauthorized
  end
end

class UsersController < AuthenticatedController
  def show
    render json: current_user
  end
end

当我请求 Users#show 时,它呈现 {"error":"text"} 就好了。

但是如果我把unauthenticated改成

  def unauthenticated(_error)
    render json: { error: 'text' }, status: :unauthorized
  end

资源响应 {"status":500,"error":"Internal Server Error"}

堆栈跟踪:

Started GET "/api/v1/user" for ::1 at 2021-08-25 12:24:30 +0300
Processing by Api::V1::UsersController#show as JSON
  Parameters: {"user"=>{}}
Completed 500 Internal Server Error in 1ms (ActiveRecord: 0.0ms | Allocations: 214)

InvalidCredentials (InvalidCredentials):
app/controllers/users_controller.rb:in `show'
app/controllers/authenticated_controller.rb:in `current_user'
app/controllers/authenticated_controller.rb:in `unauthenticated'

因此在处理程序中调用 render json: {} 会导致再次引发相同的错误。但同时 render plain: '' 没有。

当我将密钥从 plain 更改为 json 时会发生什么,以及在使用 JSON 时如何使其响应 :unauthorized 而不是服务器错误?

根据您提供的信息,问题很可能是您如何使用 rescue_from 触发错误的请求必须是 JSON 请求才能使其正常工作。例如,如果您尝试从表单登录,默认情况下通常是 HTMl 请求。确认这一点的一种方法是在控制器方法中使用 raise request.inspect。如果是这种情况,则没有简单的修复方法,您将不得不自行修复请求或同时支持两者。

我正在使用 active_model_serializers 库进行渲染 JSON。 它使用 renderscope 选项作为要调用的方法名称,它是 current_user by default。因此,当我调用 render json: ... 导致错误时,库再次调用了 current_user 方法。

我已将 unauthenticated 方法更改为

  def unauthenticated(_error)
    render json: { error: 'text' }, status: :unauthorized, scope: nil
  end

它开始按预期工作。