The joy of PSGI middleware
I must admit that I rarely feel comfortable with (web application) frameworks - each system facilitates a certain type of task but it complicates the missing parts. Good frameworks support extension via plugins, but each framework has its own plugin architecture to learn. That's why I like PSGI so much - it only specifies how to connect things. This is how PSGI looks like to me (image CC-BY-SA by CMG Lee):
Once you understand the PSGI specification you can build applications with existing bricks and create new bricks. Tatsuhiko Miyagawa has described using and creating Plack middleware in his 2009 advent calendar. The path of a request through a stack of middleware layers can be illustrated with an onion:
By now there are more than 200 PSGI middlewares classes at CPAN. An organized registry of PSGI middleware modules would help. I'd propose the following classification for the primary action that the middleware does to request and response flow:
- ↩ middleware returns a response (e.g. Plack::Middleware::Static)
- ⇄ middleware passes request and response unchanged (e.g. Plack::Middleware::SimpleLogger)
- ⇸ middleware modifies the request (e.g. Plack::Middleware::Rewrite)
- ⇷ middleware modifies the response (e.g. Plack::Middleware::ETag)
- ⇶ middleware routes to different applications (e.g. Plack::Builder)
In my opinion a useful PSGI middleware should concentrate one one simple task instead of building yet another framework. In the last couple of days I added some middleware modules that can be used independently (like all middleware modules) or combined:
- Plack::Middleware::Auth::AccessToken (↩) - Authentification via OAuth Bearer Token
- Plack::Middleware::Negotiate (⇸, ⇶) - HTTP Content negotiation
- Plack::Middleware::JSONP::Headers (⇷) - Wrap HTTP Headers in JSON response, as implemented in github API
- Plack::Middleware::SuppressResponseCodes (⇷) - Set HTTP Status code 200 for errors
- Plack::Middleware::REST - (⇶,↩) Route or reject requests based on CRUDL verbs
The source code of each module is rather short (around one third for code, documentation, and unit tests each) and the modules are quite new. Feedback is welcome, for instance github pull requests or comments how to further simplify.
What do you think about the "the shorter and more focused, the better" approach to creating PSGI middleware modules?
I wonder how you'd classify Plack::Middleware::Apache2::ModSSL in your system?
It doesn't modify the request or the response, but adds data to $env.
I'd say that Plack::Middleware::Apache2::ModSSL modifies the request or passes it unchanged (⇄,⇸) depending on what
vars
you configure it with. Many middlewares add or modify "secondary" parts of the request, for instance Plack::Middleware::SimpleLogger modifiespsgix.logger
. I'd say that the "core" request is build of all uppercase env keys listed in the PSGI spec pluspsgi.url_scheme
andpsgi.input
only.Sure the classification is only one dimension to organize middlewares. I does not tell why or how the request and/or response are passed, modified or routed, but it should be enough to create a nice railroad diagram of your PSGI stack.