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

Rails集成Cancancan后Ajax自定义动作失效,点击后重定向至首页

问题描述

我有一个Rails应用,已集成Devise和Cancancan实现身份验证与权限控制。此前自定义的search动作可正常运行,但现在点击单选按钮提交表单时,浏览器控制台显示页面被重定向到首页。

提交表单的JavaScript代码:

$("input[name='period']").click(function(){
if ($(this).val() == '1' || $(this).val() == '2' || $(this).val() == '3') {
  $('#form_search_dates').hide();
  var elem = $('#my-form')[0];
  $(elem).submit();
} 
else {
  $('#form_search_dates').show();
}
});

控制器动作代码:

class ShoppingsController < ApplicationController
  before_action :authenticate_user!
  load_and_authorize_resource

  rescue_from CanCan::AccessDenied do |exception|
    redirect_to root_url, :alert => exception.message
  end

  def search
    @shoppings = nil
    logger.debug "Dentro Search"
    @period = params[:period]
    if @period.present?
      logger.debug "Period: #{@period}"
      if @period == '1'
        logger.debug "Tutte"
        @shoppings = Shopping.where(:user_id => current_user.id).order(date_shopping: :asc)
      elsif @period == '2'
        logger.debug "Ultimi 7 giorni"
        @date_end = Date.today
        @date_start = @date_end - 7
        @shoppings = Shopping.where(:user_id => current_user.id).where(:date_shopping => @date_start..@date_end).order(date_shopping: :asc)
      elsif @period == '3'
        logger.debug "Ultimi 30 giorni"
        @date_end = Date.today
        @date_start = @date_end - 30
        @shoppings = Shopping.where(:user_id => current_user.id).where(:date_shopping => @date_start..@date_end).order(date_shopping: :asc)
        end
      else
        logger.debug "No parametri"
        @shoppings = Shopping.where(:user_id => current_user.id).order(date_shopping: :asc)
      end
   @result_total_price = @shoppings.sum(:total_price)
   respond_to do |format|
     format.html
     format.js
   end
 end
end

ability.rb配置:

def initialize(user)
  can :create, Shopping
  can :read, Shopping, user_id: user.id
  can :update, Shopping, user_id: user.id
  can :destroy, Shopping, user_id: user.id
end

点击单选按钮后的Rails控制台日志:

Started POST "/shoppings/search" for ::1 at 2023-12-19 12:18:00 +0100
Processing by ShoppingsController#search as JS
Parameters: {"authenticity_token"=>"[FILTERED]", "period"=>"2"}
User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
Redirected to http://localhost:3000/
Completed 200 OK in 17ms (ActiveRecord: 0.5ms | Allocations: 2903)

控制台未显示控制器内的调试日志,动作似乎未执行,请问遗漏了什么?

问题分析与解决方案

问题出在Cancancan的权限控制上:

  1. load_and_authorize_resource会默认对控制器的所有动作执行权限检查,包括自定义的search动作,但你的ability.rb里没有定义:search动作的权限,触发了CanCan::AccessDenied异常,被rescue_from捕获后重定向到首页,导致控制器的search代码根本没执行。
  2. 另外,load_and_authorize_resource默认会尝试加载Shopping资源,而search不是RESTful标准动作,也会引发资源加载的问题。

有两种解决方式:

方式一:排除search动作的自动权限检查

修改控制器的load_and_authorize_resource配置,排除search动作:

class ShoppingsController < ApplicationController
  before_action :authenticate_user!
  load_and_authorize_resource except: [:search] # 排除search动作

  # ... 其他代码不变
end

因为你在search动作里已经通过current_user.id过滤了数据,加上authenticate_user!已经确保用户登录,所以不需要额外的权限控制。

方式二:在ability.rb中添加search动作的权限

在ability.rb里明确允许当前用户执行search动作:

def initialize(user)
  can :create, Shopping
  can :read, Shopping, user_id: user.id
  can :update, Shopping, user_id: user.id
  can :destroy, Shopping, user_id: user.id
  can :search, Shopping # 允许所有登录用户执行search,或者加上条件can :search, Shopping, user_id: user.id
end

这种方式适合需要对search动作做更细粒度权限控制的场景。

验证修改后,控制台应该会输出你添加的调试日志,search动作也能正常执行,不会再被重定向到首页。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 18:52:42