`

rails3之ActionController

    博客分类:
  • ruby
阅读更多
rails3中有许多变化,现在也只能看一点说一点了,趁现在还有点心情,赶快把它整理写下来。
对于ActionController来说,一个比较明显的改进就是对respond_to的处理,这主要涉及到REST方面的处理。
比如在rails2中:
def index
    @users = User.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @users }
    end
end

在rails3中则可以进行简写:
def index
    @users = User.all
    respond_with(@users)
end

当然还要在application_controller.rb中加句代码:
class ApplicationController < ActionController::Base
  respond_to :html, :xml
  protect_from_forgery
end

从rail2.3开始,protect_from_forgery默认便是开启状态。至于此代码的作用以前也没在意,在网上找了一下:
引用

protect_from_forgery - A feature in Rails that protects against Cross-site Request Forgery (CSRF) attacks. This feature makes all generated forms have a hidden id  field. This id field must match the stored id or the form submission is not accepted. This prevents malicious forms on other sites or forms inserted with XSS from submitting to the Rails application.

当然有些方法会麻烦点:
def create
    @user = User.new(params[:user])

    respond_to do |format|
      if @user.save
        format.html { redirect_to(@user, :notice => 'userwas successfully created.') }
        format.xml  { render :xml => @user, :status => :created, :location => @product }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
      end
    end
  end

这个方法处理起来就要麻烦一点了:
   def create
    @user= User.new(params[:user])
    respond_with(@user) do |format|
      if @user.save
        flash[:notice] = "userwas created successfully."
        format.html { redirect_to @user}
      else
        format.html { render :action => :new }
      end
    end
  end

因为验证通过才创建对象,否则返回原来创建的页面并显示相应的错误信息。这个地方有个小问题,那就是通过REST的方式来操作即以xml的方式请求时,返回不了相应的错误信息。
而且上面的方法并没有简化多少,毕竟输出xml或html格式的代码大多都是可重复利用的,因此rails可以做得更好,rails3的确做到了,那就是ActionController::Responder,这个类完美的解决了输出格式的问题。此类文档里面有说明:
引用

# Using Rails default responder, a POST request for creating an object could
  # be written as:
  #
  #   def create
  #     @user = User.new(params[:user])
  #     flash[:notice] = 'User was successfully created.' if @user.save
  #     respond_with(@user)
  #   end
  #
  # Which is exactly the same as:
  #
  #   def create
  #     @user = User.new(params[:user])
  #
  #     respond_to do |format|
  #       if @user.save
  #         flash[:notice] = 'User was successfully created.'
  #         format.html { redirect_to(@user) }
  #         format.xml { render ml => @user, :status => :created, :location => @user }
  #       else
  #         format.html { render :action => "new" }
  #         format.xml { render ml => @user.errors, :status => :unprocessable_entity }
  #       end
  #     end
  #   end

因此创建方法将变得非常的简单:
  def create
    @user  = User.new(params[:user])
    flash[:notice] = 'User was successfully created.' if @user.save
    respond_with(@user,:location=>users_url)
  end

当然这个方法简化了很多,似乎有些神奇,主要过程就是通过respond_with调用ActionController::Responder内部相应的方法,然后。。,然后该干嘛干嘛,就不用管了。rails里面就是有这么多神奇的地方,要明白其中的原理就有些高深了,反正读rails源码要比读java框架的源码难得多,因为封装得太深了。
如果要手动控制跳转的路径可以这样:
  def create
    @user = User.new(params[:user])
    flash[:notice] = "User was created successfully." if @user.save
    respond_with(@user) do |format|
      format.html { redirect_to users_url }
    end
  end

当然更简单的方式是这样的:
  def create
    @user = User.new(params[:user])
    flash[:notice] = "User was created successfully." if @user.save
    respond_with(@user, :location => users_url)
  end

也就是说redirect_to大概与location的作用差不多。上面这段代码中users_url是一个比较有意思的东西,现在还不能很好解释清楚,虽然没有明确的定义,但它确实是有值的,上面代码的意思表示创建一用户成功后跳转到显示整个users列表的页面,即./users页面。如果不要那个:location=>users_url,则会跳转到显示显示单个user的页面。如果要细究的话,这个respond_with是处理方式还是很复杂的,看着别人用不会有问题,如果稍微变一下就会有问题。主要是用来处理rest的数据交互,不然的话直接用redirect_to,这个方式就不那么让人困惑。
当然respond_with也有:status与:head等属性。比如:
 def destroy
    @product = Product.find(params[:id])
    @product.destroy

    respond_to do |format|
      format.html { redirect_to(products_url) }
      format.xml  { head :ok }
    end
  end

可以写成:
  def destroy
    @product = Product.find(params[:id])
    @product.destroy
    respond_with(@product,:location=>products_url,:head => :ok)
  end

不过有个问题,在IE7中删除这个功能居然不能用,应该是JS的问题,在Firefox没什么问题,由于开始测试的时候用的时IE7,让本人郁闷了不少时间。
现在想想可能是html5,rails3好像采用了html5的特性,IE7怎么可能支持呢?

这篇文章只是简单的介绍了一下respond_with,如果要想知道这些action的跳转方式及跳转原理的话,那又会是一个相当麻烦的话题,涉及的内容很多。同时在rails3中也有比较大的变化,总之rails的URL跳转封装得太深,虽然用起来很方便,但理解起来却比较困难。

最近发现别看着rails有许多强大的特性,但如果要明白它的实现原理的话,还是非常复杂的,可能比java还要复杂得多,毕竟struts,spring,hibernate实现机制相对来说容易理解一些,java框架的实现总体来说是比较平滑的,而rails则不同,里面有很多激进的方式,跨度很大,而且版本变更也很大,理解起来要困难得多。
参考文章:
http://weblog.rubyonrails.org/2009/8/31/three-reasons-love-responder
http://blog.plataformatec.com.br/2009/08/embracing-rest-with-mind-body-and-soul/
http://ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with/
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics