Because the Rack interface is so simple, you can use any code that implements this interface in a Rack application.
This allows you to build small, focused, and reusable applications which work together to provide different functionalities. These mini-components are known as Middleware.
Middleware sits between the user and the application code. When an HTTP request comes in, the middleware can intercept, examine, and modify it. Similarly, it can examine and modify the HTTP response before forwarding it to the user.
Middleware is very useful for writing logic that is not specific to your web application, such as authenticating the request, logging, or exception handling. It focuses on doing one thing and doing it well.
For example, here’s a middleware that logs to the console
before it passes the request to the next application in the pipeline and
after it receives the response on its way out.
class Logger
def initialize(app)
@app = app
end
def call(env)
puts "Received the incoming request"
# forward the request to the next middleware or app
status, headers, body = @app.call(env)
puts "Received the outgoing response"
[status, headers, body]
end
end
This middleware accepts the
app
in the constructor. Theapp
can be another middleware in the pipeline or the actual application.When
call
ed, it first deals with the incoming request represented by theenv
object. Here, we print “Received the incoming request”.After handling the request, it passes the request to the next middleware (or app) in the pipeline,
Upon receiving the response (
status
,headers
,body
) from the next middleware (@app
), it logs to the console.Finally, it passes the response to the next middleware in the pipeline. This can be the middleware that called our logging middleware or the web server.
Here’s a diagram that will help you visualize the middleware pipeline.
The Rack gem provides many such components that you can use. All these components use the same Rack interface.
Here are some middleware components included in the Rack gem.
Rack::Files
, for serving static files.Rack::Config
, for modifying the environment before processing the request.Rack::ContentLength
, for setting content-length header based on body size.Rack::ContentType
, for setting default content-type header for responses.Rack::Deflater
, for compressing responses with gzip.Rack::Head
, for returning an empty body for HEAD requests.Rack::Logger
, for setting a logger to handle logging errors.Rack::Reloader
, for reloading files if they have been modified.Rack::ShowException
, for catching unhandled exceptions and presenting them in a nice and helpful way with clickable ‘backtrace.Rack::ShowStatus
, for using nice error pages for empty client error responses.
You can find a complete list here. Pick and choose whatever you like, in any way you wish.