【问题标题】:How do I show an error for unauthorized can can access如何显示未经授权的错误可以访问
【发布时间】:2014-10-16 08:07:53
【问题描述】:

我正在使用 Bootstrap,它有 div class="alert notice",其中有一堆用于各种通知消息的类。

我还有一个用于评论的 AJAX 销毁操作,我已经添加了cancan 授权。当我尝试删除 current_user 无权访问的评论时,它不起作用 - 这是正确的。

但我想要的是弹出一条错误消息,在 Bootstrap 样式的 div 中 5 - 10 秒然后消失。

这是我的CommentsController.rb 上的destroy 操作

  def destroy
    respond_to do |format|
      if @comment.destroy
          format.html { redirect_to root_url, notice: 'Comment was successfully deleted.'  }
          format.json { head :no_content }
          format.js   { render :layout => false }      
      else
          format.json { render json: @comment.errors, status: :unprocessable_entity }  
      end
    end        
  end

我在同一控制器的私有方法中设置了@comment

  private
    def set_comment
      @comment = current_user.comments.find(params[:id])
    end

这是我的comments/destroy.js.erb

$('.delete_comment').bind('ajax:success', function() {  
        $(this).closest('div#new_comment').fadeOut();
});  

但这并不影响未经授权的访问。

在我的ability.rb 中,我有这个:

can :manage, Comment, user_id: user.id

在我的日志中,当我尝试删除我无权访问的评论时,我会在我的日志中看到:

Started DELETE "/comments/5" for 127.0.0.1 at 2014-10-16 02:56:53 -0500
Processing by CommentsController#destroy as JS
  Parameters: {"id"=>"5"}
  User Load (0.4ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
  FamilyTree Load (0.2ms)  SELECT  "family_trees".* FROM "family_trees"  WHERE "family_trees"."user_id" = $1 LIMIT 1  [["user_id", 1]]
  ReadMark Load (0.1ms)  SELECT  "read_marks".* FROM "read_marks"  WHERE "read_marks"."user_id" = $1 AND "read_marks"."readable_type" = 'PublicActivity::ORM::ActiveRecord::Activity' AND "read_marks"."readable_id" IS NULL  ORDER BY "read_marks"."id" ASC LIMIT 1  [["user_id", 1]]
  Comment Load (0.3ms)  SELECT  "comments".* FROM "comments"  WHERE "comments"."user_id" = $1 AND "comments"."id" = $2 LIMIT 1  [["user_id", 1], ["id", 5]]
Completed 404 Not Found in 8ms

ActiveRecord::RecordNotFound - Couldn't find Comment with 'id'=5 [WHERE "comments"."user_id" = $1]:

这是完美的。

我想要做的就是在 Bootstrap 警报中显示一个适当的错误,该错误会在几秒钟内消失。

我该如何做到这一点?

【问题讨论】:

    标签: jquery ruby-on-rails ruby-on-rails-4 twitter-bootstrap-3 cancan


    【解决方案1】:

    首先,如果您使用 cancan - 只需使用 cancan:

    #app/controllers/comments_controller.rb
    class CommentsController < ApplicationController
      load_and_authorize_resource #this will set @comment by your ability and authorize action
      ...
    end
    

    这将引发 CanCan::AccessDenied 而不是 ActiveRecord::RecordNotFound 错误。

    让我们在ApplicationController 中使用rescue_from 捕获它

    #app/controllers/application_controller.rb
    class ApplicationController < ActionController::Base
      ...
      rescue_from CanCan::AccessDenied do |exception|
        @error_message = exception.message
        respond_to do |f|
          f.js{render 'errors/error', status: 401}
        end
      end
    end
    

    对于弹出通知,我使用 PNotify 库 http://sciactive.github.io/pnotify/ 它会在右上角显示错误,然后隐藏。 只需将其包含在您的项目中,您就可以显示如下错误:

    #app/views/errors/error.js.erb
    new PNotify({
      title: 'Oh No!',
      text: '<%=@error_message%>',
      type: 'error'
    });
    

    此代码可让您避免捕获 ActiveRecord::RecordNotFound 错误作为不良做法。

    更新

    我忘记了什么!您必须删除 set_comment 方法和 before_action 或这样写:

    before_action :set_comment
    ...
    private
    def set_comment
      @comment ||= current_user.comments.find(params[:id])
    end
    

    此回调在您的代码中覆盖了来自 load_and_authorize_resource 的 @comment 变量。 Cancan 不需要这个助手,因为它通过 load_and_authorize_resource 加载资源

    更新2

    您还需要确保您使用的是来自CanCanCommunity 的带有rails4 的最新版本的cancan,因为原来的旧版本不支持rails4

    在你的 Gemfile 中使用它

    gem 'cancancan', '~> 1.9'
    

    而不是

    gem 'cancan'
    

    【讨论】:

    • 我喜欢这个....但是,它对我不起作用。我的日志中仍然出现ActiveRecord::RecordNotFound 错误,前端没有任何反应。顺便说一句,我正在使用pnotify-rails gem - github.com/navinpeiris/pnotify-rails
    • 我应该在before_action :set_comment 之前还是之后做load_and_authorize_resource
    • 您可以删除before_action :set_commentload_and_aurthorize_resource 做同样的工作。
    • 当我这样做时,授权根本不起作用 - 即我被允许删除我不应该删除的东西。这是否意味着我的ability.rb 规则不够用?
    【解决方案2】:

    在您的comments/destroy.js.erb 文件中添加以下代码:

    <% if defined?(@error) %>
      $('.alert.notice').text("<%= @error %>");
      $('.alert.notice').show();
      setTimeout(function() {
        $('.alert.notice').fadeOut('fast');
      }, 5000);
    <% end %>
    

    set_comment方法改成使用find_by,避免出现RecordNotFound异常:

    def set_comment
      @comment = current_user.comments.find_by(id:params[:id])
    end
    

    然后在您的控制器中,您可以按如下方式修改销毁操作:

    def destroy
    respond_to do |format|
      if @comment.present?
          @comment.destroy 
          format.html { redirect_to root_url, notice: 'Comment was successfully deleted.'  }
          format.json { head :no_content }
          format.js   { render :layout => false }      
      else
          format.html { redirect_to root_url, notice: 'unauthorized access error.'  }
          format.js { @error = 'unauthorized access error' }  
      end
    end        
    

    结束

    【讨论】:

    • 这很好,很优雅,但对我不起作用。我仍然收到 ActiveRecord 错误。
    • 好的,请查看我的更新解决方案以解决 ActiveRecord 错误
    • @railsme 实际上帮我弄清楚了。这是一个康康舞曲。我需要使用 cancancan。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-13
    • 1970-01-01
    • 2015-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多