Rack相关知识梳理(二)
一、Rack中间件
1、什么是中间件
中间件其实就是Ruby应用服务器和Rack应用程序之间执行的代码
2、一个简单的例子
$LOAD_PATH.unshift(File.dirname(__FILE__)) require 'rack' require 'decorator' my_app = lambda { |env| request = Rack::Request.new(env) response = Rack::Response.new response["Content-Type"] = "text/html" if request.path_info == "/hello" response.write("you say hello") else response.write("you need say something") end response.finish } Rack::Handler::WEBrick.run Decorator.new(my_app), :Port => 3000
这段代码中我们的rack应用程序是一个lambda(lambda具有call方法的) 前面几行没什么好说的,大家看最后一句:
Rack::Handler::WEBrick.run Decorator.new(my_app), :Port => 3000
我们最后传给run的是Decorator对象,这个对象在构造时接受一个rack应用为参数,这个实例既然能被Handler调用,可想而知,我们Decorator必然具备call方法,所以我们也就清楚这个类的结构如下:
class Decorator def initialize(app) ...... end def call(env) ...... end end
ok,接下来我们编写Decorator
class Decorator def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) new_body = "=======beader=========<br/>" body.each { |str| new_body << str } new_body << "<br/>======footer=========" headers["Content-Length"] = new_body.bytesize.to_s [status, headers, [new_body]] end end
启动应用,在浏览器上看看效果:
=======beader=========
you say hello
======footer=========
由上可知,Decorator运行在Ruby应用服务器和Rack应用程序之间,因此Decorator就是一个中间件,而且我们可以知道,任何Rack中间件必然是一个合法的Rack应用程序。
3、为什么要用中间件
中间件可以实现通用逻辑和业务逻辑分离,并且这些通用逻辑可以应用到各种各样的业务逻辑中。
二、装配中间件
前面我看到一个Rack中间件必然是一个合法的Rack应用程序,那么我们不难想到,一个中间件 外面可以在包装一个中间件,每一个中间件都是独立的,只关心自己的逻辑实现,这样我们就 可以对整个框架或系统中的中间件进行替换,我们还可以用不同的方式去组合多个中间件,从 而满足我们的需求。
1、如何装配中间件
我们往往会在一个应用程序中使用多个中间件,最直接的办法就是new出我们需要的中间件,
例如我们要用middleware1和middleware2两个中间件,那我们可以这样编写代码:
Rack::Handler::WEBrick.run middleware1.new(middleware2.new(rack_app)), :Port => 3000
当然这并不是一个好办法,如果我们要用的中间件非常多,那这段代码就会非常冗⻓,好在Rack已经想到这一点,并且提供了一个非常好的办法:
Rack::Builder
我们利用Builder修改我们之前的代码:
app = lambda { |env| request = Rack::Request.new(env) response = Rack::Response.new response["Content-Type"] = "text/html" if request.path_info == "/hello" response.write("you say hello") else response.write("you need say something") end response.finish } my_app = Rack::Builder.new { use Decorator run app }.to_app Rack::Handler::WEBrick.run my_app, :Port => 3000
如果要用多个中间件,那就写成这样:
use middleware1
use middleware2
user middleware3
......
PS:前面大家可能注意到,中间件采用了装饰器的设计模式,所以当使用多个中间件时,各个中间件之间顺序是需要注意的
我们来看看Builder中几个重要的方法
initialize
def initialize(default_app = nil,&block) @use, @map, @run, @warmup = [], nil, default_app, nil instance_eval(&block) if block_given? end
initialize:构造方法,能够接受语句块
use
def use(middleware, *args, &block) if @map mapping, @map = @map, nil @use << proc { |app| generate_map app, mapping } end @use << proc { |app| middleware.new(app, *args, &block) } end
use:记录要创建的中间件及其顺序。
run
def run(app) @run = app end
run:记录原始的应用程序
to_app
def to_app app = @map ? generate_map(@run, @map) : @run fail "missing run or map statement" unless app app = @use.reverse.inject(app) { |a,e| e[a] } @warmup.call(app) if @warmup app end
to_app:根据use和run中的纪录组合最终的应用程序