首页 > 解决方案 > Rails 渲染 JSON 导致辅助方法被调用两次

问题描述

当我从 Rails 应用程序渲染 JSON 时,我遇到了奇怪的行为。一个辅助方法在render :json被调用时会运行两次。这是控制器和方法:

class UsersController < ApplicationController
  def current
      render json: { :errors => "Incorrect credentials" }, :status => :bad_request
  end
end

我有以下帮助模块,带有puts调试语句:

module SessionsHelper
  def current_user
    puts "current_user"
    if encrypted_id = request.headers[:user_id]
      user = User.find_by(id: EncryptionService.decrypt(encrypted_id))
      if user && user.authenticated?(request.headers[:remember_token])
        @curent_user = user
      end
    end
  end
end

SessionsHelper 包含在应用程序控制器中

class ApplicationController < ActionController::API
  include SessionsHelper
end

发送请求后,我得到以下信息:

Started GET "/user/current" for ::1 at 2021-02-12 22:06:47 -0800
Processing by UsersController#current as */*
  Parameters: {"user"=>{}}
current_user
[active_model_serializers] Rendered ActiveModel::Serializer::Null with Hash (0.06ms)
Completed 400 Bad Request in 1ms (Views: 0.7ms | ActiveRecord: 6.6ms | Allocations: 383)

current_user被打印了,即使该函数从未被调用过。当我注释掉render json:声明时,离开:

class UsersController < ApplicationController
  def current
  end
end

我得到以下信息:

 Started GET "/user/current" for ::1 at 2021-02-12 22:09:43 -0800
Processing by UsersController#current as */*
  Parameters: {"user"=>{}}
Completed 204 No Content in 0ms (ActiveRecord: 4.2ms | Allocations: 78)

current_user不打印。为什么会render json:打电话current_user?在我的实际应用程序中,这会导致数据库被命中两次(尽管 Rails 明智地缓存了结果)。

更新:我在这里做点什么。我跑去puts caller[0]看看是谁在调用这个函数。结果: /Users/izaguirrejoe/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/active_model_serializers-0.10.12/lib/action_controller/serialization.rb:40:in 'serialization_scope'

    def serialization_scope
      return unless _serialization_scope && respond_to?(_serialization_scope, true)

      send(_serialization_scope)
    end

有任何想法吗?

标签: ruby-on-railsjson

解决方案


我看到您正在使用 active_model_serializers,如果您查看他们在此处所说的文档,则默认序列化范围是:current_user. 它还强调,

重要提示:由于范围是在渲染时设置的,您可能需要对其进行自定义,以便不会在每个请求上调用 current_user。这也是 0.9 中的一个问题。

这导致该current_user方法总是被调用。如果你想避免这种行为,你可以serialization_scope在控制器中设置例如:

class UsersController < ApplicationController
  serialization_scope nil # also you can pass a custom method here

  def current
    render json: { :errors => "Incorrect credentials" }, :status => :bad_request
  end
end

或者在某些情况下仅通过调用self.class.serialization_scope nil.


推荐阅读