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中的纪录组合最终的应用程序
 
posted @ 2022-05-12 00:09  阿拉懒神灯  阅读(258)  评论(0编辑  收藏  举报