首页 > Web开发, 动态语言, 挨踢(IT) > 《Agile Web Development with Rails》抄书笔记(12):错误处理

《Agile Web Development with Rails》抄书笔记(12):错误处理

2013年6月13日 发表评论 阅读评论 206 人阅读    

《Agile Web Development with Rails》抄书笔记系列

  “《Agile Web Development with Rails》抄书笔记系列”目录

  上一节,我们介绍了如何将购物车中相同的商品合并到一条记录中。还让大家寻找购物车实现的缺点。大家有木有什么发现啊?

  请大家注意,向购物车添加商品后,购物车页面的URL,格式是否如cart/nnn,其中,nnn是购物车的ID。如果我是一个居心叵测的捣蛋鬼,我填入一个非法的字符,会显示什么结果呢?D瓜哥试验的结果是, Active Record 会报一个 RecordNotFound 的异常,进一步会显示应用的调用信息、框架信息以及服务器相关信息。有了这些信息,那些别有用心的捣蛋鬼就可以实施有针对性的攻击,甚至破坏我们的应用等。所以,要针对这些错误做一些特殊处理。这就引出了我们这节的主要内容:错误处理

D呱呱

  关于这节内容的代码:

  1. 这节开始之前:https://github.com/diguage/depot/tree/v-10.1
  2. 这节完成之后:https://github.com/diguage/depot/tree/v-10.2

  针对这些错误信息,我们该如何错误呢?根据安全准则之一,如果遇到错误,最好让其在服务器后台就默默消失(仅指在客户端页面显示上),不给潜在的攻击者一点有帮助的信息。针对前台页面展示,可以将错误信息默默消失,但是对于后台日志却要显示出错误信息。这就是针对错误处理的全部要点。

  明白了错误处理的全部要点,那么我们就在我们的程序中实现出来。

  根据上面的表述,我们可以确定,当我们遇到错误是,有两步是必须做的:1、使用Rails提供的日志机制,将错误日志记录到日志文件中;2、将页面重新定位到网站的首页,同时给出一个错误提示,告知用户发生错误了,这样用户就能继续使用我们的网站。

  Rails提供了非常方便的日志处理和日志报告功能。Rails在内部定义一种被称为flash的结构,flash就像一个水桶,它能存储你在处理一个请求时所需的数据,在被自动删除前,还可以被同一个会话(Session)的下次请求使用。一个典型的用法就是,用来收集错误信息。例如,当我们的show()方法探测到一个非法的购物车ID时,它能将错误信息存放到flash中去,然后重定向到index() Action中去展示商品信息。同时,index的展示层中可以提取错误信息然后在商品列表顶部展示出来。可以通过方法访问flash中的信息。

  但是,我们为什么不使用变量来存储错误信息呢?!这里就要给大家解释一下,当应用程序发送一个重定给浏览器后,浏览器会重新发起一个新的请求给应用程序。当应用程序再次接受这个新请求时,应用程序已经将上一个请求的变量全部清除。而flash会将信息存储到会话(Session)中,就是为了方便在两个请求之间共享数据。

  了解了所有的背景知识后,下面我们来开始改造show()方法,让它可以拦截错误的购物车ID,然后将错误信息提示给用户。打开%Depot%/app/controllers/carts_controller.rb文件,按照如下内容修改show()方法:

  def show
       begin
            @cart = Cart.find(params[:id])
       rescue ActiveRecord::RecordNotFound
            logger.error "Attempt to access invalid cart" #{params[:id]}
            redirect_to store_url, notice: 'Invalid cart'
       else
           respond_to do |format|
            format.html # show.html.erb
            format.json { render json: @cart }
          end
       end
  end

  在这段代码中,rescue语句拦截由Cart.find抛出的错误!在接下来的处理方法中,我们的代码干了如下工作:

  1. 使用Rails logger工具记录了错误日志。每一个Controller都有一个logger属性,用于记录日志。这里,我们记录error级别的错误。
  2. 使用redirect_to()方法重定向到商品列表展示页面。这个:notice属性,就是用于向flash中添加作为提示的信息。也许你回问问啥使用重新向,而且是重定向到商品列表展示页面?如果重新向,用户浏览器中显示的是商品列表的URL,而不是原来的像http://…/cart/wibble这样的错误链接。这样可以使我们的程序尽量少暴露一些错误给用户。另外,还可以防止用户刷新页面时,重新触发错误信息。

  看过原版书的朋友也许和D瓜哥看书时,有一个同样的疑问,在%Depot%/app/views/store/index.html.erb中,按照书中给出的代码,在开头有如下三行无用的代码(当然也可以猜测出来它用途,只是一直没有发现在哪里使用):

<% if notice %>
     <p id="notice"><%= notice %></p>
<% end %>

  看过D瓜哥提交的代码的朋友也许同样存在一个纳闷,为啥这这个文件的开头有如下提示:

<% #在第八章有notice这里没用,暂时不加 %>

  也行看到这里,大家都都明白了。原书中提到的三行代码就是在这里用于给用户提示的,而上面三行代码中的notice正是对应的错误处理中的参数notice也。所以,我们在这里把这个文件修改成如下:

<% if notice %>
     <p id="notice"><%= notice %></p>
<% end %>

<h1>Your Pragmatic Catalog</h1>
<% @products.each do |product| %>
     <div class="entry">
          <%= image_tag(product.image_url) %>
          <h3><%= product.title %></h3>
          <%= sanitize(product.description) %>
          <div class="price_line">
               <span class="price"><%= number_to_currency(product.price) %></span>
               <%= button_to 'Add to Cart', line_items_path(product_id: product) %>
          </div>
     </div>
<% end %>

  D瓜哥第一次看这本书的时候,一直想不通。后来,为了写这个系列的文章,重读了一遍。才想透其中的意思。书读百遍其义自现,古人不余欺也!

  经过这样的处理后,再次输入像下面这种错误链接时,http://localhost:3000/carts/wibble,我们将不会再看到应用的错误信息,取而代之的是商品列表展示页面。这里,我们可以看看日志文件(在log目录下的development.log文件)的最下面,我们会看到如下内容:

Started GET "/carts/wibble" for 127.0.0.1 at 2013-06-08 22:14:09 +0800
Processing by CartsController#show as HTML
  Parameters: {"id"=>"wibble"}
  Cart Load (0.0ms)  SELECT "carts".* FROM "carts" WHERE "carts"."id" = ? LIMIT 1  [["id", "wibble"]]
Attempt to access invalid cart
Redirected to http://localhost:3000/
Completed 302 Found in 0ms (ActiveRecord: 0.0ms)


作 者: D瓜哥,https://www.diguage.com/
原文链接:https://www.diguage.com/archives/18.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。