如何显示未经授权的可以访问的错误

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

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

但我想要发生的是弹出一个错误信息,在Bootstrap style’d 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警报中显示一个适当的错误。

我该如何做到这一点?

首先,如果你使用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错误,这是一种不好的做法。

UPDATE

我忘记了什么! 你必须删除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变量。 load_and_authorize_resource不需要此帮助程序,因为它通过load_and_authorize_resource加载资源

UPDATE2

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

只需在你的Gemfile中使用它

 gem 'cancancan', '~> 1.9' 

代替

 gem 'cancan' 

在您的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以避免获取RecordNotFoundexception:

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

然后在你的控制器中你可以修改destroy动作,如下所示:

 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 

结束

Interesting Posts