Moved middleware introduction to "Specification Overview" section,

adding a code example for a trivial middleware component.
This commit is contained in:
Phillip J. Eby 2004-09-17 15:31:58 +00:00
parent 45b26f3c1f
commit 3f6ca60005
1 changed files with 100 additions and 26 deletions

View File

@ -125,6 +125,17 @@ Other servers and gateways may use configuration files or other
mechanisms to specify where an application object should be
imported from, or otherwise obtained.
In addition to "pure" servers/gateways and applications/frameworks,
it is also possible to create "middleware" components that implement
both sides of this specification. Such components act as an
application to their containing server, and as a server to a
contained application, and can be used to provide extended APIs,
content transformation, navigation, and other useful functions.
The Application/Framework Side
------------------------------
The application object is simply a callable object that accepts
two arguments. The term "object" should not be misconstrued as
requiring an actual object instance: a function, method, class,
@ -184,6 +195,10 @@ invoking a callable must *not* have any dependency on what kind of
callable was provided to it. Callables are only to be called, not
introspected upon.
The Server/Gateway Side
-----------------------
The server or gateway invokes the application callable once for each
request it receives from an HTTP client, that is directed at the
application. To illustrate, here is a simple CGI gateway, implemented
@ -254,8 +269,89 @@ server.
if hasattr(result,'close'):
result.close()
In the next section, we will specify the precise semantics that
these illustrations are examples of.
Middleware: Components that Play Both Sides
-------------------------------------------
Note that a single object may play the role of a server with respect
to some application(s), while also acting as an application with
respect to some server(s). Such "middleware" components can perform
such functions as:
* Routing a request to different application objects based on the
target URL, after rewriting the ``environ`` accordingly.
* Allowing multiple applications or frameworks to run side-by-side
in the same process
* Load balancing and remote processing, by forwarding requests and
responses over a network
* Perform content postprocessing, such as applying XSL stylesheets
The presence of middleware in general is transparent to both the
"server/gateway" and the "application/framework" sides of the
interface, and should require no special support. A user who
desires to incorporate middleware into an application simply
provides the middleware component to the server, as if it were
an application, and configures the middleware component to
invoke the application, as if the middleware component were a
server. Of course, the "application" that the middleware wraps
may in fact be another middleware component wrapping another
application, and so on, creating what is referred to as a
"middleware stack".
For the most part, middleware must conform to the restrictions
and requirements of both the server and application sides of
WSGI. In some cases, however, requirements for middleware
are more stringent than for a "pure" server or application,
and these points will be noted in the specification.
Here is a (tongue-in-cheek) example of a middleware component that
converts ``text/plain`` responses to pig latin, using Joe Strout's
``piglatin.py``. (Note: a "real" middleware component would
probably use a more robust way of checking the content type, and
should also check for a content encoding.)
::
from piglatin import piglatin
class Latinator:
# by default, don't transform output
transform = str
def __init__(self, application):
self.application = application
def __call__(environ, start_response):
def write_latin(data):
self.write(self.transform(data))
def start_latin(status,headers,exc_info=None):
for name,value in headers:
if name.lower()=='content-type' and value=='text/plain':
self.transform = piglatin
# Strip content-length if present, else it'll be wrong
headers = [(name,value)
for name,value in headers
if name.lower()<>'content-length'
]
break
self.write = start_response(status,headers,exc_info)
return write_latin
for data in self.application(environ,start_latin):
yield self.transform(data)
# Run foo_app under a Latinator's control, using the example CGI gateway
from foo_app import foo_app
run_with_cgi(Latinator(foo_app))
Specification Details
@ -851,6 +947,8 @@ of its use::
start_response(status, headers)
return ["normal body goes here"]
except:
# XXX should trap runtime issues like MemoryError, KeyboardInterrupt
# in a separate handler before this bare 'except:'...
status = "500 Oops"
headers = [("content-type","text/plain")]
start_response(status, headers, sys.exc_info())
@ -976,30 +1074,6 @@ course, applications should cache such configuration, to avoid having
to re-read it upon each invocation.)
Middleware
----------
Note that a single object may play the role of a server with respect
to some application(s), while also acting as an application with
respect to some server(s). Such "middleware" components can perform
such functions as:
* Routing a request to different application objects based on the
target URL, after rewriting the ``environ`` accordingly.
* Allowing multiple applications or frameworks to run side-by-side
in the same process
* Load balancing and remote processing, by forwarding requests and
responses over a network
* Perform content postprocessing, such as applying XSL stylesheets
Given the existence of applications and servers conforming to this
specification, the appearance of such reusable middleware becomes
a possibility.
Supporting Older (<2.2) Versions of Python
------------------------------------------