Rack
Rack应用是位于web框架与服务器之间的一层,充当中间件的角色,方便对请求/回复做处理。
为什么用Rack
实现了Rack协议的应用可以作为插件(Plug-in)拼接在一起,方便功能组装,如:Puma服务器就可以与Rails,Sinatra或者其他实现了Rack协议的web框架配合使用。
Rack中间件
因为Rack位于请求与回复之间,多个Rack应用(实现了Rack协议的Ruby程序)就可以拼接在一起实现如下的功能:
- Logging
- Session
- Profiling(记录耗时长的请求)
- Caching
- Security(拒绝异常ip访问)
- 静态文件
在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