1Porting Routes to a WSGI Web Framework
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
4RoutesMiddleware
5----------------
6
7An application can create a raw mapper object and call its ``.match`` and
8``.generate`` methods.  However, WSGI applications probably want to use
9the ``RoutesMiddleware`` as Pylons does::
10
11    # In myapp/config/middleware.py
12    from routes.middleware import RoutesMiddleware
13    app = RoutesMiddleware(app, map)     # ``map`` is a routes.Mapper.
14
15The middleware matches the requested URL and sets the following WSGI
16variables::
17
18        environ['wsgiorg.routing_args'] = ((url, match))
19        environ['routes.route'] = route
20        environ['routes.url'] = url
21
22where ``match`` is the routing variables dict, ``route`` is the matched route,
23and ``url`` is a ``URLGenerator`` object.  In Pylons, ``match`` is used by the
24dispatcher, and ``url`` is accessible as ``pylons.url``.
25
26The middleware handles redirect routes itself, issuing the appropriate
27redirect.  The application is not called in this case.
28
29To debug routes, turn on debug logging for the "routes.middleware" logger.
30
31See the Routes source code for other features which may have been added.
32
33URL Resolution
34--------------
35
36When the URL is looked up, it should be matched against the Mapper. When
37matching an incoming URL, it is assumed that the URL path is the only string
38being matched. All query args should be stripped before matching::
39
40    m.connect('/articles/{year}/{month}', controller='blog', action='view', year=None)
41
42    m.match('/articles/2003/10')
43    # {'controller':'blog', 'action':'view', 'year':'2003', 'month':'10'}
44
45Matching a URL will return a dict of the match results, if you'd like to
46differentiate between where the argument came from you can use routematch which
47will return the Route object that has all these details::
48
49    m.connect('/articles/{year}/{month}', controller='blog', action='view', year=None)
50
51    result = m.routematch('/articles/2003/10')
52    # result is a tuple of the match dict and the Route object
53
54    # result[0] - {'controller':'blog', 'action':'view', 'year':'2003', 'month':'10'}
55    # result[1] - Route object
56    # result[1].defaults - {'controller':'blog', 'action':'view', 'year':None}
57    # result[1].hardcoded - ['controller', 'action']
58
59Your integration code is then expected to dispatch to a controller and action
60in the dict. How it does this is entirely up to the framework integrator. Your
61integration should also typically provide the web developer a mechanism to
62access the additional dict values.
63
64Request Configuration
65---------------------
66
67If you intend to support ``url_for()`` and ``redirect_to()``, they depend on a
68singleton object which requires additional configuration.  You're better off
69not supporting them at all because they will be deprecated soon.
70``URLGenerator`` is the forward-compatible successor to ``url_for()``.
71``redirect_to()`` is better done in the web framework`as in
72``pylons.controllers.util.redirect_to()``.
73
74``url_for()`` and ``redirect_to()`` need information on the current request,
75and since they can be called from anywhere they don't have direct access to the
76WSGI environment.  To remedy this, Routes provides a thread-safe singleton class
77called "request_config", which holds the request information for the current
78thread. You should update this after matching the incoming URL but before
79executing any code that might call the two functions.  Here is an example::
80
81    from routes import request_config
82
83    config = request_config()
84
85    config.mapper = m                  # Your mapper object
86    config.mapper_dict = result        # The dict from m.match for this URL request
87    config.host = hostname             # The server hostname
88    config.protocol = port             # Protocol used, http, https, etc.
89    config.redirect = redir_func       # A redirect function used by your framework, that is
90                                       # expected to take as the first non-keyword arg a single
91                                       # full or relative URL
92
93See the docstring for ``request_config`` in routes/__init__.py to make sure
94you've initialized everything necessary.
95