rails 使用 render 的时候总是会调用控制器的 current_user 方法

环境

  • OS: Manjaro 20.1 I3
  • Rails: 6.0
  • Ruby: 2.7.1

事故现场

在写登录接口的时候,发现使用render返回用户数据总是会调用到控制器里边的current_user方法。current_user的定义如下:

class ApplicationController < ActionController::API
  before_action :find_user

  def find_user
    return @user if @user

    params.require :api_token
    @user = SessionService.find_user(params[:api_token])
  end

  def current_user
    @user ||= find_user
  end

end

current_user@user为空时会调用到find_user方法,而find_user方法需要api_token参数,登录时并没有,所以代码报错。

登录的相关逻辑:

class SessionController < ApplicationController
  skip_before_action :find_user, only: [:login]

  def login
    user = User.find_by_name(user_params[:username])
    if user.authenticate(user_params[:password])
      user.login!
    else
      raise Session::AuthenticateFailed.new(I18n.t('authenticate_failed'))
    end

    render json: user # 提示这一行出错
  end

end

原因

debug:

  • render去掉之后不报错
  • render json: user 改为 render json: {a: 1} 还是报错

最后在万能的googlestackoverflow下面找到了原因:是active model serializer这个gem的锅,是这个gemrender的时候调用了控制器的current_user


解决办法

1. 在AppController声明serialization_scope:view_context

class ApplicationController < ActionController::API
  serialization_scope :view_context
  before_action :find_user

  def find_user
    return @user if @user

    params.require :api_token
    @user = SessionService.find_user(params[:api_token])
  end

  def current_user
    @user ||= find_user
  end

end

不过这样修改后,所有的serializer里边的current_user都要用view_context.current_user来替代

class AdminUserSerializer < ActiveModel::Serializer
  attributes :id, :name, :can_edit

  def can_edit?
    view_context.current_user.admin?
  end
end

2. 修改current_user方法名,如logged_user之类的


参考

Why is current_user called on render in controller?
rails-api/active_model_serializers

点赞

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注