“过滤器”(before,after,around)

“过滤器”可以让你在你的“控制器”内写代码来包装由“动作”完成的处理—你可以
写个代码块,并在你的控制器(或你的“控制器”的子类)内的任何数量的“动作”之前或之
后来调用它。这是个强大功能。使用“过滤器”,我们可以实现检验计划,日志,response
compression,甚至是定制的“应答”。

Rails 支持三种类型的“过滤器”:before,after,和around。“过滤器”可以在“动
作”运行之前,或之后被调用。这依赖于你如何定义它们,它们即可做为“控制器”内的方
法来运行,也可以在它们运行时传递“控制器”对象。不论哪种方式,它们都可以访问request的细节和response 对象,还有其它的“控制器”属性。
Before 和After “过滤器”
就像它们名字所暗示的,before 和after“过滤器”在一个“动作”之前或之后被调用。
Rails 会维护每个“控制器”内的两种过滤器的链。当一个“控制器”运行一个“动作”时,它执行所有的before 链中“过滤器”。在after 链中它在执行“动作”后运行“过滤器”。“过滤器”是被动的,它由一个“控制器”激活。它们在请求处理中也可有更多的活动
部分。如果一个before“过滤器”返回false,“过滤器”链被中止并且“动作”不会运行。
“过滤器”也可能提交输出或重定向请求,在这种情况下原有的“动作”则从不会得到调用。
我们看一下125 页中我们商店的管理功能中用于授权时使用“过滤器”的例子。我们定
义一个授权方法,如果当前的“会话”中没有登录用户,则它重定向到一个登录屏幕。
def authorize
unless session[:user_id]
flash[:notice] = "Please log in"
redirect_to(:controller => "login", :action => "login")
end
end
那么在管理者“控制器”内的所有“动作”都要使用这个before“过滤器”方法。
class AdminController < ApplicationController
before_filter :authorize
# ...
这儿有个例子,它有个方法行为类似于一个“过滤器”;我们以符号方式传递方法的名
字给before_filter。filter 也声明接受块和类名字。如果指定了一个块,它将用当前“控制器”做为一参数来被调用。如果给出类,它的filter()类方法将带有做为参数的“控制器”被调用。
class AuditFilter
def self.filter(controller)
AuditLog.create(:action => controller.action_name)
end
end
# ...
class SomeController < ApplicationController
before_filter do |controller|
logger.info("Processing #{controller.action_name}")
end
after_filter AuditFilter
# ...
end
缺省地,“过滤器”被应用于一个“控制器”(和这个“控制器”的所有子类)内的所有
“动作”。你可以修改它用选项:only,它接受一个或多个要被过滤的“动作”,:except 选项,则列出被“过滤器”排除的“动作”。
class BlogController < ApplicationController
before_filter :authorize, :only => [ :delete, :edit_comment ]
after_filter :log_access, :except => :rss
# ...
before_filter 和after_filter 声明被附加给“控制器”的“过滤器”链。使用各种
prepend_before_filter()和prepend_after_filter()方法来在链的头部放置“过滤器”。

[color=red]After Filters and Response Munging[/color]
如果需要话,after“过滤器”可以被用于修改外部“应答”,修改“头”和内容。一些
应用程序使用这个技术来完成对“控制器”的模板创建的内容全局的置换(例如,在“应答”
体内用字符串<customer/>置换一个客户的名字)。另一种用法是如果用户的浏览器支持的话,
可以压缩“应答”。
下面的代码是如何用它来工作的例子。[这个代码没有个完整的压缩实现。特别地,它没
有使用send_file()压缩下载的数据流。]“控制器”声明了compress()方法为一个after“过滤器”。方法查看“请求头”,看浏览器是否接受被压缩的请求。如果接受,它使用Zlib 库来压缩“应答”体到一个字符串内。[注意,Zlib Ruby 扩展可以在你的平台上无效—它依赖于基础libzlib.a 库的表现。] 如果结果比原有的体小,它替换压缩版本并更新“应答”编码类型。
require 'zlib'
require 'stringio'
class CompressController < ApplicationController
after_filter :compress
def index
render(:text => "<pre>" + File.read("/etc/motd") + "</pre>")
end
protected
def compress
accepts = request.env['HTTP_ACCEPT_ENCODING']
return unless accepts && accepts =~ /(x-gzip|gzip)/
encoding = $1
output = StringIO.new
def output.close # Zlib does a close. Bad Zlib...
rewind
end
gz = Zlib::GzipWriter.new(output)
gz.write(response.body)
gz.close
if output.length < response.body.length
response.body = output.string
response.headers['Content-encoding'] = encoding
end
end
end

[color=red]Around “过滤器”[/color]
除了单独的before和after过滤器之外,也可以指定一个能处理before和after两者调用的单个对象。这对在你需要在before和after之间保持状态特别有用。如下面的benchmark过滤器例子。
class WeblogController < ActionController::Base
around_filter BenchmarkingFilter.new
# 这个动作完成之前,BenchmarkingFilter#before(controller) 被执行。
def index
end
# 在这个动作已经完成之后,BenchmarkingFilter#after(controller) 执行。
end
class BenchmarkingFilter
def initialize
@runtime
end
def before
start_timer
end
def after
stop_timer
report_result
end
end
不像before 和after“过滤器”,around“过滤器”不接受:only 或:except 参数。
around“过滤器”添加不同的“过滤器”链。Around 对象的before()方法被附加到链上,
而after()方法被优先于after 链。这意味着那个around 对象将可正确地嵌套。如果你写
around_filter A.new, B.new
则“过滤器”的调用次序将是:
A#before()
B#before
action...
B#after
A#after
继承“过滤器”
如果你子类化包含“过滤器”的“控制器”,则“过滤器”就像在父对象上一样将在子
对象上运行。但是,“过滤器”若定义在子类内则就不能在父对象上运行。

A、Filter inheritance --- 过滤器继承
控制器继承层次向下共享过滤器,但子类也可以添加新的过滤器,而不会影响到超类。例如:
class BankController < ActionController::Base
before_filter :audit
private
def audit
# record the action and parameters in an audit log
end
end

class VaultController < BankController
before_filter :verify_credentials
private
def verify_credentials
# make sure the user is allowed into the vault
end
end
现在BankController内的任何动作在完成前必须调用audit方法。在VaultController内,首先audit方法被调用,然后是verify_credetials方法被调用。如果audit方法返回false,那么verify_credentials和目标动作都会被中止。
B、Filter types --- 过滤器类型
过滤器可以接受三种模式中的一个:方法引用(符号),外部类,或内联方法(proc)。第一个是最常用的,它使用一个符号来引用控制器继承体系内某处的protected或private方法来工作。在上面的bank例子中,BankController和VaultController两者都使用了种格式。
使用外部类可轻易地重复使用通用过滤器,例如输出压缩。外部过滤器类由一个含有静态的filter方法的任意类实现,然后传递这个类给filter方法。例如:
class OutputCompressionFilter
def self.filter(controller) #静态的filter方法。
controller.response.body = compress(controller.response.body)
end
end
class NewspaperController < ActionController::Base
after_filter OutputCompressionFilter
end
这个filter方法被传递一个控制器实例,因此它可以访问控制器内的所有东西,并可以操纵它们。
内联方法(使用一个proc)可以用于快速地完成一些不需要太多的解释的小巧事情。或者是做为一个快速测试。它像这样工作:
class WeblogController < ActionController::Base
before_filter { |controller| false if controller.params["stop_action"] }
end
像你看到的,块被传递控制器后,它赋值请求给内部变量。这意味着可以访问request和response两者提供的方便的params,session,template,和assigns方法。注意:内联方法不是严格要求必须有一个块的;任何对应答返回1或-1的对象都可以(如Proc或一个Method对象)。
C、Filter chain ordering --- 过滤器链的次序
before_filter和after_filter被附加给现在链的指定过滤器。这用起来很好,胆有时候你可能想调整被运行过滤器的次序,这种情况下,你可以使用prepend_before_filter和prepend_after_filter。由这些方法添加的过滤器会被放到它们各自链的开始处,并会其它过滤器之前运行。例如:
class ShoppingController
before_filter :verify_open_shop
class CheckoutController
prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
现在用于CheckoutController的过滤器链是 :ensure_items_in_cart, :ensure_items_in_stock, :verify_open_shop。所以如果ensure过滤器返回false,我们就不需要查看其它过滤器了。
你可以传递每种类型过滤器的多个参数做为一个过滤器块。如果给出一个这样的块,它会被视为大参数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值