[ruby]Rack应用

356 阅读2分钟

Rack

Rack应用是位于web框架与服务器之间的一层,充当中间件的角色,方便对请求/回复做处理。

为什么用Rack

实现了Rack协议的应用可以作为插件(Plug-in)拼接在一起,方便功能组装,如:Puma服务器就可以与Rails,Sinatra或者其他实现了Rack协议的web框架配合使用。

Rack中间件

因为Rack位于请求与回复之间,多个Rack应用(实现了Rack协议的Ruby程序)就可以拼接在一起实现如下的功能:

  1. Logging
  2. Session
  3. Profiling(记录耗时长的请求)
  4. Caching
  5. Security(拒绝异常ip访问)
  6. 静态文件

在Rails项目里,执行rake middleware查看项目中应用的Rack中间件。

Rack应用举例

Rack应用就是带有实例方法call的类。

require 'rack'

handler = Rack::Handler::Thin

class RackApp
  def call(env)
    [200, {"Content-Type" => "text/plain"}, "Hello from Rack"]
  end
end

handler.run RackApp.new

上述程序便是一个简单的Rack应用。handler.run RackApp.new会在本地的8080端口启动一个服务器。

require 'rack'

handler = Rack::Handler::Thin

class RackApp
  def call(env)
    req = Rack::Request.new(env)

    if req.ip == "5.5.5.5"
      [403, {}, ""]
    else
      [200, {"Content-Type" => "text/plain"}, "Hello from Rack"]
    end
  end
end

handler.run RackApp.new

上述程序中,针对来自IP 5.5.5.5的请求全部拒绝。如果想在Rack应用中获得请求的细节,使用Rack::Request.new(env)。可以获得如下的请求信息:

  • path_info (/articles/1)
  • ip (of user)
  • user_agent (Chrome, Firefox, Safari…)
  • request_method (get / post)
  • body (contents)
  • media_type (plain, json, html)

搭配Rack应用与Rack中间件

下面是Rack应用:

require 'rack'

handler = Rack::Handler::Thin

class RackApp
  def call(env)
    req = Rack::Request.new(env)

    [200, {"Content-Type" => "text/plain"}, "Hello from Rack - #{req.ip}"]
  end
end

下面是Rack中间件:

class FilterLocalHost
  def initialize(app)
    @app = app
  end

  def call(env)
    req = Rack::Request.new(env)

    if req.ip == "127.0.0.1" || req.ip == "::1"
      [403, {}, ""]
    else
      @app.call(env)
    end
  end
end

注意:Rack中间件的构造函数接收app参数。通过app,我们可以决定继续沿着中间件链条往下传递请求,还是中断请求,直接返回。

下面将上述的Rack应用与Rack中间件拼装在一起:

app =
Rack::Builder.new do |builder|
  builder.use FilterLocalHost
  builder.run RackApp.new
end

handler.run app

对于回复(response)的处理,如:

class UpcaseAll
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)

    response.upcase!

    [status, headers, response]
  end
end

References

Rack explained for Ruby developers

Rack spec