You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Rails 6中创建分类时控制器的SystemStackError问题

Rails 6创建分类时触发SystemStackError的诊断与解决

问题场景

在Rails 6的API控制器中创建分类时触发SystemStackError (stack level too deep),服务器日志显示分类已成功创建,但在执行render json: @category步骤时出现栈溢出,同时日志中也出现了index方法的错误回溯。

服务器日志片段

Started POST "/api/v1/categories" for ::1 at 2023-09-07 00:30:36 +0530
Processing by Api::V1::CategoriesController#create as JSON
  Parameters: {"name"=>"Electronics", "category"=>{"name"=>"Electronics"}}
Can't verify CSRF token authenticity.
  User Load (1.0ms)  SELECT "users".* FROM "users" WHERE "users"."token" IS NULL LIMIT $1  [["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:13:in `authenticate_request'
  TRANSACTION (0.9ms)  BEGIN
  ↳ app/controllers/application_controller.rb:15:in `authenticate_request'
  User Update (1.1ms)  UPDATE "users" SET "sign_in_count" = $1, "current_sign_in_at" = $2, "updated_at" = $3 WHERE "users"."id" = $4  [["sign_in_count", 2], ["current_sign_in_at", "2023-09-06 19:00:36.703908"], ["updated_at", "2023-09-06 19:00:36.704756"], ["id", 1]]
  ↳ app/controllers/application_controller.rb:15:in `authenticate_request'
  TRANSACTION (1.4ms)  COMMIT
  ↳ app/controllers/application_controller.rb:15:in `authenticate_request'
  TRANSACTION (0.8ms)  BEGIN
  ↳ app/controllers/api/v1/categories_controller.rb:17:in `create'
  Category Create (1.1ms)  INSERT INTO "categories" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "Electronics"], ["created_at", "2023-09-06 19:00:36.729039"], ["updated_at", "2023-09-06 19:00:36.729039"]]
  ↳ app/controllers/api/v1/categories_controller.rb:17:in `create'
  TRANSACTION (0.9ms)  COMMIT
  ↳ app/controllers/api/v1/categories_controller.rb:17:in `create'
  ActiveStorage::Attachment Load (1.8ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4  [["record_id", 2], ["record_type", "Category"], ["name", "image"], ["LIMIT", 1]]
  ↳ app/controllers/api/v1/categories_controller.rb:18:in `create'
Completed 500 Internal Server Error in 85ms (ActiveRecord: 21.6ms | Allocations: 40064)
 
SystemStackError (stack level too deep):
  
app/controllers/api/v1/categories_controller.rb:18:in `create'

**SystemStackError (stack level too deep):

app/controllers/api/v1/categories_controller.rb:7:in `index'

相关控制器代码

Api::V1::CategoriesController

class Api::V1::CategoriesController < ApplicationController
    before_action :set_category, only: [:show, :update, :destroy]
   
      def index
        @categories = Category.all
        # render json: @categories
      end

      def show
        render json: @category
      end

      def create
        @category = Category.new(category_params)

        if @category.save
          render json: @category, status: :created
        else
          render json: @category.errors, status: :unprocessable_entity
        end
      end

      def update
        if @category.update(category_params)
          render json: @category
        else
          render json: @category.errors, status: :unprocessable_entity
        end
      end

      def destroy
        @category.destroy
        # head :no_content
      end

      private

      def set_category
        @category = Category.find(params[:id])
      end

      def category_params
        params.require(:category).permit(:name, :image)
      end

end

ApplicationController

class ApplicationController < ActionController::Base
  include JsonWebToken
  protect_from_forgery with: :null_session

  before_action :authenticate_request
  before_action :configure_permitted_parameters, if: :devise_controller?
  attr_reader :current_user

  private
  
  def authenticate_request
    token = request.headers['Authorization']&.split(' ')&.last
    user = User.find_by(token: token)
    if user.present?
      sign_in user, store: false
    else
      render json: { error: 'Invalid token' }, status: :unauthorized
    end
  end

  protected

  def configure_permitted_parameters
    added_attrs = [:mobile_number, :email, :first_name, :last_name]
    devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
    devise_parameter_sanitizer.permit :account_update, keys: added_attrs
  end
  
end

原因诊断

  1. JSON序列化循环引用:这是最常见的栈溢出原因。如果Category模型定义了自关联(如父分类/子分类的belongs_to :parent和has_many :children),直接调用render json: @category会触发递归序列化——父分类包含子分类,子分类又包含父分类,无限循环直到栈溢出。
  2. ActiveStorage附件序列化问题:模型中has_one_attached :image的序列化逻辑可能存在隐性循环,比如自定义的附件处理方法触发了重复的模型序列化。
  3. index方法未显式渲染:index方法中注释了render json: @categories,Rails会尝试寻找对应的视图模板,若模板不存在或模板中存在递归逻辑,可能引发异常(日志中的index错误大概率是栈溢出后的回溯干扰)。

解决步骤

1. 排查并修复序列化循环引用

  • 检查Category模型的关联定义,若存在自关联,修改render语句排除循环字段:
    # 在create方法中修改
    render json: @category, except: [:parent_id, :created_at, :updated_at], status: :created
    # 若需要包含子分类,需限制子分类的序列化字段避免循环
    render json: @category, include: { children: { except: [:parent_id] } }, status: :created
    
  • 推荐使用Active Model Serializers自定义序列化规则,彻底避免循环:
    生成序列化器:
    rails generate serializer Category
    
    编辑序列化器,只定义需要返回的字段:
    class CategorySerializer < ActiveModel::Serializer
      attributes :id, :name
      # 若需要包含子分类,添加以下配置(排除parent关联)
      # has_many :children, serializer: CategorySerializer
    end
    
    在控制器中使用序列化器:
    render json: @category, serializer: CategorySerializer, status: :created
    

2. 修复index方法的渲染逻辑

取消index方法中render json: @categories的注释,避免Rails自动查找视图模板引发异常:

def index
  @categories = Category.all
  render json: @categories
end

3. 调试ActiveStorage附件问题

临时替换create方法的render语句,只返回基础字段,验证是否是附件序列化导致的问题:

render json: { id: @category.id, name: @category.name }, status: :created

若此操作不再报错,说明image附件的序列化存在问题,可检查模型中是否有自定义的附件回调或方法,或在序列化器中排除附件字段,按需单独返回附件URL。

4. 排查Devise登录逻辑

虽然日志中authenticate_request执行正常,但sign_in user, store: false可能触发了用户模型的回调,若用户模型与分类模型存在关联且回调中有递归逻辑,也可能引发栈溢出。可临时注释sign_in语句验证是否解决问题,再逐步排查回调逻辑。


内容的提问来源于stack exchange,提问作者Prakash Chand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:36:01