今天在研究rails3发错误电子信的时候无意中发现了强大的rack middleware,一开始还不知道,后来才发现了它强大的功能。先看看rack吧:(以下也是来自网络)
Rack是什么?Rack提供了用ruby开发web应用的一个接口。比如Rails框架,就是由rack承担着跟web服务器之间的交互。简而言 之,Rack已经成为ruby开发web应用程序的一个规范,它统一了web服务器和web框架之间的交互接口。它所支持的web服务器和web框架已经 非常之多:http://rack.rubyforge.org/doc/ 。
Rack的规范非常简单,就是一个call方法:http://rack.rubyforge.org/doc/SPEC.html 。接受一个environment,然后返回status,header和body。这不就是http的全部么?
A Middleware is a simple class instance that has two methods (at least), initialize
and call
. It is used to alter(改变) a HTTP request to and the response from your main Rack-compliant(兼容 ) web application. It is a form of proxy but with code rather than separate software. A set of middleware and their respective endpoints are known as a Stack.(一个 中间件及 各自 的端点 设置 被称为 堆栈。 )
An Endpoint is again a simple ruby class that only responds to call
. Thus it is so similar to a middleware that in fact a class can be one and the same thing, if it wants to return a response instead of letting it cascade through to the main endpoint.
Almost the simplest middleware possible is the following:
class SimpleMiddleware def initialize(app, options={}) @app = app @options = options end def call(env) # downward logic status, headers, response = @app.call(env) # upward logic [status, headers, response] end end
Firstly, the initialize
method, which accepts two arguments. The first is a rack endpoint, and the second is some options that will be required later for some logic.
The other important feature is the call
method, which accepts a hash of environment variables sent from the middleware further out. It also returns an array consisting of three things: the status, a hash of headers and a response body.
As an example, here is a basic (imaginary) middleware stack:
# Top of the chain: the Listening Server use SecondSimpleMiddleware # Inherits from SimpleMiddleware use SimpleMiddleware run ActionController::Routing::Routes # Bottom of the chain: Your App
When a HTTP request comes in from a client, the server call
s SecondSimpleMiddleware, which executes its downward logic, then call
s SimpleMiddleware. SimpleMiddleware in turn executes it’s own downward logic, and call
s ActionController::Routing::Routes, which executes your applications logic.
Cleverly, ActionController::Routing::Routes returns an array containing 3 important elements. The first is the http status, as an integer. The second is a hash of headers to send back in the response. The last is the response body, which should respond to each
.
This array gets returned to SimpleMiddleware, which performs its upward logic before returning an array in the exact same format to SecondSimpleMiddleware. Then SecondSimpleMiddleware performs its upward logic, before returning an array of the exact same format back to the listening server, which formats it as a HTTP Response and sends it to the client.
class EarlyResponderMiddleware def initialize(app, options={}) @app = app @options = options end def call(env) if env['path'] == "/foo" body = "Not Found" [404, {"Content-Length"=> body.length}, body] else @app.call(env) end end end
And the following is what would happen if the path matched “/foo”:
The beauty of this implementation is that middlewares can be added, moved and removed from this stack at will, without any other middlewares giving a damn. The exact implementation has been made so simple yet clever that an endpoint actually looks exactly the same as an endpoint with a middleware in front of it.
The following is the build_app
method I mentioned earlier, which Rack calls to construct its app
object to hand to a server.
def build_app(app) middleware[options[:environment]].reverse_each do |middleware| middleware = middleware.call(self) if middleware.respond_to?(:call) next unless middleware klass = middleware.shift app = klass.new(app, *middleware) end app end
In Rails 3, the default middleware stack is:
use ActionDispatch::Static # only development # part two use Rack::Lock use ActiveSupport::Cache::Strategy::LocalCache use Rack::Runtime use Rails::Rack::Logger # part three use ActionDispatch::ShowExceptions use ActionDispatch::RemoteIp use Rack::Sendfile use ActionDispatch::Callbacks # part four use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore # part five use ActionDispatch::Flash use ActionDispatch::ParamsParser use Rack::MethodOverride use ActionDispatch::Head run Test::Application.routes # part six
This stack is very easy to alter using the config.middleware methods. They allow you to insert middleware anywhere in the stack. More information can be found in the “Rails on Rack” Rails Guide . You’ll also find a lot more API centric information about middleware in that article, whereas my articles are looking more at the implementation.
为什么不可以在Rack和web framework之间做些事情呢?于是Rack middleware发展起来了。Rack中间件其实就是一些遵循Rack规范的应用。为什么要发展出Rack middleware?因为很多事情在这里做起来就非常方便,其实就是AOP的方式(你也可以把它理解成filter)。
看一下众多的middlewares:http://wiki.github.com/rack/rack/list-of-middleware 。或者在你的最新(2.3)rails应用下敲下命令行:rake middleware。
关联:http://andyhu1007.iteye.com/blog/537610
Rails provides a simple configuration interface config.middleware for adding, removing and modifying the middlewares in the middleware stack via environment.rb or the environment specific configuration file environments/<environment>.rb .
You can add a new middleware to the middleware stack using any of the following methods:
Example:
# config/environment.rb # Push Rack::BounceFavicon at the bottom config.middleware.use Rack::BounceFavicon # Add Lifo::Cache after ActiveRecord::QueryCache. # Pass { :page_cache => false } argument to Lifo::Cache. config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, :page_cache => false
You can swap an existing middleware in the middleware stack using config.middleware.swap .
Example:
# config/environment.rb # Replace ActionController::Failsafe with Lifo::Failsafe config.middleware.swap ActionController::Failsafe, Lifo::Failsafe
The middleware stack behaves just like a normal Array . You can use any Array methods to insert, reorder, or remove items from the stack. Methods described in the section above are just convenience methods.
For example, the following removes the middleware matching the supplied class name:
config.middleware.delete(middleware)
Much of Action Controller’s functionality is implemented as Middlewares. The following table explains the purpose of each of them:
Rack::Lock | Sets env["rack.multithread"] flag to true and wraps the application within a Mutex. |
ActionController::Failsafe | Returns HTTP Status 500 to the client if an exception gets raised while dispatching. |
ActiveRecord::QueryCache | Enable the Active Record query cache. |
ActionController::Session::CookieStore | Uses the cookie based session store. |
ActionController::Session::MemCacheStore | Uses the memcached based session store. |
ActiveRecord::SessionStore | Uses the database based session store. |
Rack::MethodOverride | Sets HTTP method based on _method parameter or env["HTTP_X_HTTP_METHOD_OVERRIDE"] . |
Rack::Head | Discards the response body if the client sends a HEAD request. |