summaryrefslogtreecommitdiff
path: root/docs/narr/urldispatch.rst
blob: 05c04e03f8d57bcce962a11f14f89bc12ab5f134 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
.. index::
   single: URL dispatch

.. _urldispatch_chapter:

URL Dispatch
============

The URL dispatch feature of :mod:`repoze.bfg` allows you to either
augment or replace :term:`traversal` as a :term:`context finding`
mechanism, allowing URL pattern matching to have the "first crack" at
resolving a given URL to :term:`context` and :term:`view name`.

Although it is a "context-finding" mechanism, ironically, using URL
dispatch exclusively allows you to avoid thinking about your
application in terms of "contexts" and "view names" entirely.

Many applications don't need :mod:`repoze.bfg` features -- such as
declarative security via an :term:`authorization policy` -- that
benefit from having any visible separation between :term:`context
finding` and :term:`view lookup`.  To this end, URL dispatch provides
a handy syntax that allows you to effectively map URLs *directly* to
:term:`view` code in such a way that you needn't think about your
application in terms "context finding" at all.  This makes developing
a :mod:`repoze.bfg` application seem more like developing an
application in a system that is "context-free", such as :term:`Pylons`
or :term:`Django`.

Whether or not you care about "context", it often makes a lot of sense
to use :term:`URL dispatch` instead of :term:`traversal` in an
application that has no natural data hierarchy.  For instance, if all
the data in your application lives in a relational database, and that
relational database has no self-referencing tables that form a natural
hierarchy, URL dispatch is easier to use than traversal, and is often
a more natural fit for creating an application that manipulates "flat"
data.

The presence of :ref:`route_directive` statements in a :term:`ZCML`
file used by your application or the presence of calls to the
:meth:`repoze.bfg.configuration.Configurator.add_route` method in
imperative configuration within your application is a sign that you're
using :term:`URL dispatch`.

High-Level Operational Overview
-------------------------------

If route configuration is present in an application, the
:mod:`repoze.bfg` :term:`Router` checks every incoming request against
an ordered set of URL matching patterns present in a *route map*.

If any route pattern matches the information in the :term:`request`
provided to :mod:`repoze.bfg`, a route-specific :term:`context` and
:term:`view name` will be generated.  In this circumstance,
:mod:`repoze.bfg` will shortcut :term:`traversal`, and will invoke
:term:`view lookup` using the context and view name generated by URL
dispatch.  If the matched route names a :term:`view callable` in its
configuration, that view callable will be invoked when view lookup is
performed.

However, if no route pattern matches the information in the
:term:`request` provided to :mod:`repoze.bfg`, it will fail over to
using :term:`traversal` to perform context finding and view lookup.

Route Configuration
-------------------

:term:`Route configuration` is the act of adding a new :term:`route`
to an application.  A route has a *path*, representing a pattern meant
to match against the ``PATH_INFO`` portion of a URL, and a *name*,
which is used by developers within a :mod:`repoze.bfg` application to
uniquely identify a particular route when generating a URL.  It also
optionally has a ``factory``, a set of :term:`route predicate`
parameters, and a set of :term:`view` parameters.

A route configuration may be added to the system via :term:`imperative
configuration` or via :term:`ZCML`.  Both are completely equivalent.

.. index::
   single: add_route

Configuring a Route Imperatively via The ``add_route`` Configurator Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :meth:`repoze.bfg.configuration.Configurator.add_route` method
adds a single :term:`route configuration` to the :term:`application
registry`.  Here's an example:

.. ignore-next-block
.. code-block:: python

   # "config" below is presumed to be an instance of the
   # repoze.bfg.configuration.Configurator class; "myview" is assumed
   # to be a "view callable" function
   from views import myview
   config.add_route(name='myroute', path='/prefix/:one/:two', view=myview)

.. index::
   single: ZCML directive; route

Configuring a Route via ZCML
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Instead of using the imperative
:meth:`repoze.bfg.configuration.Configurator.add_route` method to add
a new route, you can alternately use :term:`ZCML`.  For example, the
following :term:`ZCML declaration` causes a route to be added to the
application.

.. code-block:: xml
   :linenos:

   <route
       name="myroute"
       path="/prefix/:one/:two"
       view=".views.myview"
    />

.. note::

   Values prefixed with a period (``.``) within the values of ZCML
   attributes such as the ``view`` attribute of a ``route`` mean
   "relative to the Python package directory in which this
   :term:`ZCML` file is stored".  So if the above ``route``
   declaration was made inside a ``configure.zcml`` file that lived in
   the ``hello`` package, you could replace the relative
   ``.views.myview`` with the absolute ``hello.views.myview`` Either
   the relative or absolute form is functionally equivalent.  It's
   often useful to use the relative form, in case your package's name
   changes.  It's also shorter to type.


See :ref:`route_directive` for full ``route`` ZCML directive
documentation.

Route Configuration That Names a View Callable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When a route configuration declaration names a ``view`` attribute, the
attribute will be a value that references a :term:`view callable`.  A
view callable, as described in :ref:`views_chapter`, is
developer-supplied code that "does stuff" as the result of a request.
For more information about how to create view callables, see
:ref:`views_chapter`.

Here's an example route configuration that references a view callable:

.. code-block:: xml
   :linenos:

   <route
       name="myroute"
       path="/prefix/:one/:two"
       view="mypackage.views.myview"
    />

When a route configuration names a ``view`` attribute, the :term:`view
callable` named as that ``view`` attribute will always be found and
invoked when the associated route path pattern matches during a
request.  

The purpose of making it possible to specify a view callable within a
route configuration is to prevent developers from needing to deeply
understand the details of :term:`context finding` and :term:`view
lookup`.  When a route names a view callable, and a request enters the
system which matches the path of the route, the result is simple: the
view callable associated with the route is invoked with the request
that caused the invocation.

For most usage, you needn't understand more than this; how it works is
an implementation detail.  In the interest of completeness, however,
we'll explain how it *does* work in the following section.  You can
skip it if you're uninterested.

Route View Callable Registration and Lookup Details
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

When a ``view`` attribute is attached to a route configuration,
:mod:`repoze.bfg` ensures that a :term:`view configuration` is
registered that will always be found when the route path pattern is
matched during a request.  To do so:

- A special route-specific :term:`interface` is created at startup time
  for each route configuration declaration.

- When a route configuration declaration mentions a ``view``
  attribute, a :term:`view configuration` is registered at startup
  time.  This view configuration uses the route-specific interface as
  a :term:`request` type.

- At runtime, when a request causes any route to match, the
  :term:`request` object is decorated with the route-specific
  interface.

- The fact that the request is decorated with a route-specific
  interface causes the view lookup machinery to always use the view
  callable registered using that interface by the route configuration
  to service requests that match the route path pattern.

In this way, we supply a shortcut to the developer.  Under the hood,
:mod:`repoze.bfg` still consumes the :term:`context finding` and
:term:`view lookup` subsystems provided by :mod:`repoze.bfg`, but in a
way which does not require that a developer understand either of them
if he doesn't want or need to.  It also means that we can allow a
developer to combine :term:`URL dispatch` and :term:`traversal` in
various exceptional cases as documented in :ref:`hybrid_chapter`.

.. index::
   single: route path pattern syntax

.. _route_path_pattern_syntax:

Route Path Pattern Syntax
~~~~~~~~~~~~~~~~~~~~~~~~~

The syntax of the pattern matching language used by :mod:`repoze.bfg`
URL dispatch in the *path* argument is straightforward; it is close to
that of the :term:`Routes` system used by :term:`Pylons`.

The *path* used in route configuration may start with a slash
character.  If the path does not start with a slash character, an
implicit slash will be prepended to it at matching time.  For example,
the following paths are equivalent:

.. code-block:: text

   :foo/bar/baz

and:

.. code-block:: text

   /:foo/bar/baz

A path segment (an individual item between ``/`` characters in the
path) may either be a literal string (e.g. ``foo``) *or* it may
segment replacement marker (e.g. ``:foo``).  A segment replacement
marker is in the format ``:name``, where this means "accept any
characters up to the next slash and use this as the ``name`` matchdict
value."  For example, the following pattern defines one literal
segment ("foo") and two dynamic segments ("baz", and "bar"):

.. code-block:: text

   foo/:baz/:bar

The above pattern will match these URLs, generating the following
matchdicts:

.. code-block:: text

   foo/1/2        -> {'baz':u'1', 'bar':u'2'}
   foo/abc/def    -> {'baz':u'abc', 'bar':u'def'}

It will not match the following patterns however:

.. code-block:: text

   foo/1/2/        -> No match (trailing slash)
   bar/abc/def     -> First segment literal mismatch

Note that values representing path segments matched with a
``:segment`` match will be url-unquoted and decoded from UTF-8 into
Unicode within the matchdict.  So for instance, the following
pattern:

.. code-block:: text

   foo/:bar

When matching the following URL:

.. code-block:: text

   foo/La%20Pe%C3%B1a

The matchdict will look like so (the value is URL-decoded / UTF-8
decoded):

.. code-block:: text

   {'bar':u'La Pe\xf1a'}

If the pattern has a ``*`` in it, the name which follows it is
considered a "remainder match".  A remainder match *must* come at the
end of the path pattern.  Unlike segment replacement markers, it does
not need to be preceded by a slash.  For example:

.. code-block:: text

   foo/:baz/:bar*fizzle

The above pattern will match these URLs, generating the following
matchdicts:

.. code-block:: text

   foo/1/2/           -> {'baz':1, 'bar':2, 'fizzle':()}
   foo/abc/def/a/b/c  -> {'baz':abc, 'bar':def, 'fizzle':('a', 'b', 'c')}

Note that when a ``*stararg`` remainder match is matched, the value
put into the matchdict is turned into a tuple of path segments
representing the remainder of the path.  These path segments are
url-unquoted and decoded from UTF-8 into Unicode.  For example, for
the following pattern:

.. code-block:: text

   foo/*fizzle

When matching the following path:

.. code-block:: text

   /foo/La%20Pe%C3%B1a/a/b/c

Will generate the following matchdict:

.. code-block:: text

   {'fizzle':(u'La Pe\xf1a', u'a', u'b', u'c')}

.. index::
   single: route ordering

Route Declaration Ordering
~~~~~~~~~~~~~~~~~~~~~~~~~~

Because route configuration declarations are evaluated in a specific
order when a request enters the system, route configuration
declaration ordering is very important.

The order that routes declarations are evaluated is the order in which
they are added to the application at startup time.  This is unlike
:term:`traversal`, which depends on emergent behavior which happens as
a result of traversing a graph.

The order that route are evaluated when they are defined via
:term:`ZCML` is the order in which they appear in the ZCML relative to
each other.  For routes added via the
:mod:`repoze.bfg.configuration.Configurator.add_route` method, the
order that routes are evaluated is the order in which they are added
to the configuration imperatively.

For example, route configuration statements with the following
patterns might be added in the following order:

.. code-block:: text

   members/:def
   members/abc

In such a configuration, the ``members/abc`` pattern would *never* be
matched; this is because the match ordering will always match
``members/:def`` first; the route configuration with ``members/abc``
will never be evaluated.

.. index::
   single: route factory

Route Factories
~~~~~~~~~~~~~~~

A "route" configuration declaration can mention a "factory".  When
that route matches a request, and a factory is attached to a route,
the :term:`root factory` passed at startup time to the
:term:`Configurator` is ignored; instead the factory associated with
the route is used to generate a :term:`root` object.  This object will
usually be used as the :term:`context` of the view callable ultimately
found via :term:`view lookup`.

.. code-block:: xml

   <route
    path="/abc"
    name="abc"
    view=".views.theview"
    factory=".models.root_factory"
    />

In this way, each route can use a different factory, making it
possible to supply a different :term:`context` object to the view
related to each particular route.

Supplying a different context for each route is useful when you're
trying to use a :mod:`repoze.bfg` :term:`authorization policy` to
provide declarative "context-sensitive" security checks; each context
can maintain a separate :term:`ACL`, as in
:ref:`using_security_with_urldispatch`.  It is also useful when you
wish to combine URL dispatch with :term:`traversal` as documented
within :ref:`hybrid_chapter`.

Route Configuration Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Route configuration statements may specify a large number of
arguments.

Many of these arguments are :term:`route predicate` arguments.  A
route predicate argument specifies that some aspect of the request
must be true for the associated route to be considered a match during
the route matching process.

Other arguments are view configuration related arguments.  These only
have an effect when the route configuration names a ``view``.

Other arguments are ``name`` and ``factory``.  These are required
arguments but represent neither a predicate nor view configuration
information.

**Non-Predicate Arguments**

``name``
  The name of the route, e.g. ``myroute``.  This attribute is
  required.  It must be unique among all defined routes in a given
  application.

``factory``
  A reference to a Python object (often a function or a class) that
  will generate a :mod:`repoze.bfg` :term:`context` object when this
  route matches. For example, ``mypackage.models.MyFactoryClass``.  If
  this argument is not specified, the traversal root factory will be
  used.

**Predicate Arguments**

``path``
  The path of the route e.g. ``ideas/:idea``.  This argument is
  required.  See :ref:`route_path_pattern_syntax` for information
  about the syntax of route paths.  If the path doesn't match the
  current URL, route matching continues.

``xhr``
  This value should be either ``True`` or ``False``.  If this value is
  specified and is ``True``, the :term:`request` must possess an
  ``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header for this
  route to match.  This is useful for detecting AJAX requests issued
  from jQuery, Prototype and other Javascript libraries.  If this
  predicate returns ``False``, route matching continues.

``request_method``
  A string representing an HTTP method name, e.g. ``GET``, ``POST``,
  ``HEAD``, ``DELETE``, ``PUT``.  If this argument is not specified,
  this route will match if the request has *any* request method.  If
  this predicate returns ``False``, route matching continues.

``path_info``
  This value represents a regular expression pattern that will be
  tested against the ``PATH_INFO`` WSGI environment variable.  If the
  regex matches, this predicate will return ``True``.  If this
  predicate returns ``False``, route matching continues.

``request_param``
  This value can be any string.  A view declaration with this argument
  ensures that the associated route will only match when the request
  has a key in the ``request.params`` dictionary (an HTTP ``GET`` or
  ``POST`` variable) that has a name which matches the supplied value.
  If the value supplied as the argument has a ``=`` sign in it,
  e.g. ``request_params="foo=123"``, then the key (``foo``) must both
  exist in the ``request.params`` dictionary, and the value must match
  the right hand side of the expression (``123``) for the route to
  "match" the current request.  If this predicate returns ``False``,
  route matching continues.

``header``
  This argument represents an HTTP header name or a header name/value
  pair.  If the argument contains a ``:`` (colon), it will be
  considered a name/value pair (e.g. ``User-Agent:Mozilla/.*`` or
  ``Host:localhost``).  If the value contains a colon, the value
  portion should be a regular expression.  If the value does not
  contain a colon, the entire value will be considered to be the
  header name (e.g. ``If-Modified-Since``).  If the value evaluates to
  a header name only without a value, the header specified by the name
  must be present in the request for this predicate to be true.  If
  the value evaluates to a header name/value pair, the header
  specified by the name must be present in the request *and* the
  regular expression specified as the value must match the header
  value.  Whether or not the value represents a header name or a
  header name/value pair, the case of the header name is not
  significant.  If this predicate returns ``False``, route matching
  continues.

``accept``
  This value represents a match query for one or more mimetypes in the
  ``Accept`` HTTP request header.  If this value is specified, it must
  be in one of the following forms: a mimetype match token in the form
  ``text/plain``, a wildcard mimetype match token in the form
  ``text/*`` or a match-all wildcard mimetype match token in the form
  ``*/*``.  If any of the forms matches the ``Accept`` header of the
  request, this predicate will be true.  If this predicate returns
  ``False``, route matching continues.

``custom_predicates``
  This value should be a sequence of references to custom predicate
  callables.  Use custom predicates when no set of predefined
  predicates does what you need.  Custom predicates can be combined
  with predefined predicates as necessary.  Each custom predicate
  callable should accept two arguments: ``context`` and ``request``
  and should return either ``True`` or ``False`` after doing arbitrary
  evaluation of the context and/or the request.  If all callables
  return ``True``, the associated route will be considered viable for
  a given request.  If any custom predicate returns ``False``, route
  matching continues.  Note that the value ``context`` will always be
  ``None`` when passed to a custom route predicate.

**View-Related Arguments**

``view``
  A reference to a Python object that will be used as a view callable
  when this route matches. e.g. ``mypackage.views.my_view``.
  
``view_context``
  A reference to a class or an :term:`interface` that the
  :term:`context` of the view should match for the view named by the
  route to be used.  This argument is only useful if the ``view``
  attribute is used.  If this attribute is not specified, the default
  (``None``) will be used.

  If the ``view`` argument is not provided, this argument has
  no effect.

  This attribute can also be spelled as ``for_`` or ``view_for``.

``view_permission``
  The permission name required to invoke the view associated with this
  route.  e.g. ``edit``. (see :ref:`using_security_with_urldispatch`
  for more information about permissions).

  If the ``view`` attribute is not provided, this argument has
  no effect.

  This argument can also be spelled as ``permission``.

``view_renderer``
  This is either a single string term (e.g. ``json``) or a string
  implying a path or :term:`resource specification`
  (e.g. ``templates/views.pt``).  If the renderer value is a single
  term (does not contain a dot ``.``), the specified term will be used
  to look up a renderer implementation, and that renderer
  implementation will be used to construct a response from the view
  return value.  If the renderer term contains a dot (``.``), the
  specified term will be treated as a path, and the filename extension
  of the last element in the path will be used to look up the renderer
  implementation, which will be passed the full path.  The renderer
  implementation will be used to construct a response from the view
  return value.  See :ref:`views_which_use_a_renderer` for more
  information.

  If the ``view`` argument is not provided, this argument has
  no effect.

  This argument can also be spelled as ``renderer``.

``view_request_type``
  A reference to an :term:`interface` representing a :term:`request
  type`.  If this argument is not specified, any request type will be
  considered a match for the view associated with this route.

  If the ``view`` argument is not provided, this argument has
  no effect.

  This argument can also be spelled as ``request_type``.

``view_containment``
  This value should be a reference to a Python class or
  :term:`interface` that a parent object in the :term:`lineage` must
  provide in order for the view related to this route to be found and
  called.  Your models must be 'location-aware' to use this feature.
  See :ref:`location_aware` for more information about
  location-awareness.

  If the ``view`` argument is not provided, this argument has no
  effect.

``view_attr``
  The view machinery defaults to using the ``__call__`` method of the
  view callable (or the function itself, if the view callable is a
  function) to obtain a response dictionary.  The ``attr`` value
  allows you to vary the method attribute used to obtain the response.
  For example, if your view was a class, and the class has a method
  named ``index`` and you wanted to use this method instead of the
  class' ``__call__`` method to return the response, you'd say
  ``attr="index"`` in the view configuration for the view.  This is
  most useful when the view definition is a class.

  If the ``view`` argument is not provided, this argument has no
  effect.

Route Matching
--------------

The main purpose of route configuration is to match (nor not match)
the ``PATH_INFO`` present in the WSGI environment provided during a
request against a URL path pattern.

The way that :mod:`repoze.bfg` does this is very simple.  When a
request enters the system, for each route configuration declaration
present in the system, :mod:`repoze.bfg` checks the ``PATH_INFO``
against the pattern declared.

If any route matches, the route matching process stops.  The
:term:`request` is decorated with a special :term:`interface` which
describes it as a "route request", the :term:`context` and :term:`view
name` are generated, and the context, the view name, and the resulting
request are handed off to :term:`view lookup`.  This process is
otherwise known as :term:`context finding`.  During view lookup, if
any ``view`` argument was provided within the matched route
configuration, the :term:`view callable` it points to is called.

When a route configuration is declared, it may contain :term:`route
predicate` arguments.  All route predicates associated with a route
declaration must be ``True`` for the route configuration to be used
for a given request.

If any predicate in the set of :term:`route predicate` arguments
provided to a route configuration returns ``False``, that route is
skipped and route matching continues through the ordered set of
routes.

If no route matches after all route patterns are exhausted,
:mod:`repoze.bfg` falls back to :term:`traversal` to do :term:`context
finding` and :term:`view lookup`.


.. index::
   single: matchdict

The Matchdict
~~~~~~~~~~~~~

When the URL path pattern associated with a particular route
configuration is matched by a request, a dictionary named
``matchdict`` is added as an attribute of the :term:`request` object.
Thus, ``request.matchdict`` will contain the values that match
replacement patterns in the ``path`` element.  The keys in a matchdict
will be strings.  The values will be Unicode objects.

.. note::

   If no route URL pattern matches, no ``matchdict`` is attached to
   the request.

Routing Examples
----------------

Let's check out some examples of how route configuration statements
might be commonly declared, and what will happen if a they are matched
by the information present in a request.  The examples that follow
assume that :term:`ZCML` will be used to perform route configuration,
although you can use :term:`imperative configuration` equivalently if
you like.

.. _urldispatch_example1:

Example 1
~~~~~~~~~

The simplest route declaration which configures a route match to
*directly* result in a particular view callable being invoked:

.. code-block:: xml
   :linenos:

   <route
    name="idea"
    path="site/:id"
    view="mypackage.views.site_view"
    />

When a route configuration with a ``view`` attribute is added to the
system, and an incoming request matches the *path* of the route
configuration, the :term:`view callable` named as the ``view``
attribute of the route configuration will be invoked.

In the case of the above example, when the URL of a request matches
``/site/:id``, the view callable at the Python dotted path name
``mypackage.views.site_view`` will be called with the request.  In
other words, we've associated a view callable directly with a route
path.

When the ``/site/:id`` route path pattern matches during a request,
the ``site_view`` view callable is invoked with that request as its
sole argument.  When this route matches, a ``matchdict`` will be
generated and attached to the request as ``request.matchdict``.  If
the specific URL matched is ``/site/1``, the ``matchdict`` will be a
dictionary with a single key, ``id``; the value will be the string
``'1'``, ex.: ``{'id':'1'}``.

The ``mypackage.views`` module referred to above might look like so:

.. code-block:: python
   :linenos:

   from webob import Response

   def site_view(request):
       return Response(request.matchdict['id'])

The view has access to the matchdict directly via the request, and can
access variables within it that match keys present as a result of the
route path pattern.

See :ref:`views_chapter` for more information about views.

Example 2
~~~~~~~~~

Below is an example of a more complicated set of route statements you
might add to your application:

.. code-block:: xml
   :linenos:

   <route
    name="idea"
    path="ideas/:idea"
    view="mypackage.views.idea_view"
    />

   <route
    name="user"
    path="users/:user"
    view="mypackage.views.user_view"
    />

   <route 
    name="tag" 
    path="tags/:tag"
    view="mypackage.views.tag_view"
    />

The above configuration will allow :mod:`repoze.bfg` to service URLs
in these forms:

.. code-block:: text

   /ideas/:idea
   /users/:user
   /tags/:tag

- When a URL matches the pattern ``/ideas/:idea``, the view available
  at the dotted Python pathname ``mypackage.views.idea_view`` will be
  called.  For the specific URL ``/ideas/1``, the ``matchdict``
  generated and attached to the :term:`request` will consist of
  ``{'idea':'1'}``.  

- When a URL matches the pattern ``/users/:user``, the view available
  at the dotted Python pathname ``mypackage.views.user_view`` will be
  called.  For the specific URL ``/users/1``, the ``matchdict``
  generated and attached to the :term:`request` will consist of
  ``{'user':'1'}``.

- When a URL matches the pattern ``/tags/:tag``, the view available
  at the dotted Python pathname ``mypackage.views.tag_view`` will be
  called.  For the specific URL ``/tags/1``, the ``matchdict``
  generated and attached to the :term:`request` will consist of
  ``{'tag':'1'}``.

In this example we've again associated each of our routes with a
:term:`view callable` directly.  In all cases, the request, which will
have a ``matchdict`` attribute detailing the information found in the
URL by the process will be passed to the view callable.

Example 3
~~~~~~~~~

The context object passed in to a view found as the result of URL
dispatch will, by default, be an instance of the object returned by
the :term:`root factory` configured at startup time (the
``root_factory`` argument to the :term:`Configurator` used to
configure the application).

You can override this behavior by passing in a ``factory`` argument to
the ZCML directive for a particular route.  The ``factory`` should be
a callable that accepts a :term:`request` and returns an instance of a
class that will be the context used by the view.

An example of using a route with a factory:

.. code-block:: xml
   :linenos:

   <route
    name="idea"
    path="ideas/:idea"
    view=".views.idea_view"
    factory=".models.Idea"
    />

The above route will manufacture an ``Idea`` model as a
:term:`context`, assuming that ``mypackage.models.Idea`` resolves to a
class that accepts a request in its ``__init__``.  For example:

.. code-block:: python
   :linenos:

   class Idea(object):
       def __init__(self, request):
           pass

In a more complicated application, this root factory might be a class
representing a :term:`SQLAlchemy` model.

Example 4
~~~~~~~~~

It is possible to create a route declaration without a ``view``
attribute, but associate the route with a :term:`view callable` using
a ``view`` declaration.

.. code-block:: xml
   :linenos:

   <route
    name="idea"
    path="site/:id"
    />

   <view
    view="mypackage.views.site_view"
    route_name="idea"
    />

This set of configuration parameters creates a configuration
completely equivalent to this example provided in
:ref:`urldispatch_example1`:

.. code-block:: xml
   :linenos:

   <route
    name="idea"
    path="site/:id"
    view="mypackage.views.site_view"
    />

In fact, the spelling which names a ``view`` attribute is just
syntactic sugar for the more verbose spelling which contains separate
view and route registrations.

More uses for this style of associating views with routes are explored
in :ref:`hybrid_chapter`.

.. index::
   single: matching the root URL
   single: root url (matching)

Matching the Root URL
---------------------

It's not entirely obvious how to use a route path pattern to match the
root URL ("/").  To do so, give the empty string as a path in a ZCML
``route`` declaration:

.. code-block:: xml
   :linenos:

   <route
       path=""
       name="root"
       view=".views.root_view"
       />

Or provide the literal string ``/`` as the path:

.. code-block:: xml
   :linenos:

   <route
       path="/"
       name="root"
       view=".views.root_view"
       />

.. index::
   single: generating route URLs
   single: route URLs

Generating Route URLs
---------------------

Use the :func:`repoze.bfg.url.route_url` function to generate URLs
based on route paths.  For example, if you've configured a route in
ZCML with the ``name`` "foo" and the ``path`` ":a/:b/:c", you might do
this.

.. ignore-next-block
.. code-block:: python
   :linenos:

   from repoze.bfg.url import route_url
   url = route_url('foo', request, a='1', b='2', c='3')

This would return something like the string
``http://example.com/1/2/3`` (at least if the current protocol and
hostname implied ``http:/example.com``).  See the
:func:`repoze.bfg.url.route_url` API documentation for more
information.

.. index::
   single: redirecting to slash-appended routes

Redirecting to Slash-Appended Routes
------------------------------------

For behavior like Django's ``APPEND_SLASH=True``, use the
:func:`repoze.bfg.view.append_slash_notfound_view` view as the
:term:`Not Found view` in your application.  When this view is the Not
Found view (indicating that no view was found), and any routes have
been defined in the configuration of your application, if the value of
``PATH_INFO`` does not already end in a slash, and if the value of
``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP
redirect to the slash-appended ``PATH_INFO``.

Let's use an example, because this behavior is a bit magical.  If this
your route configuration is looks like so, and the
``append_slash_notfound_view`` is configured in your application:

.. code-block:: xml
   :linenos:

   <route
     view=".views.no_slash"
     path="no_slash"
    />

   <route
     view=".views.has_slash"
     path="has_slash/"
    />

If a request enters the application with the ``PATH_INFO`` value of
``/no_slash``, the first route will match.  If a request enters the
application with the ``PATH_INFO`` value of ``/no_slash/``, *no* route
will match, and the slash-appending "not found" view will *not* find a
matching route with an appended slash.

However, if a request enters the application with the ``PATH_INFO``
value of ``/has_slash/``, the second route will match.  If a request
enters the application with the ``PATH_INFO`` value of ``/has_slash``,
a route *will* be found by the slash appending notfound view.  An HTTP
redirect to ``/has_slash/`` will be returned to the user's browser.

Note that this will *lose* ``POST`` data information (turning it into
a GET), so you shouldn't rely on this to redirect POST requests.

To configure the slash-appending not found view in your application,
change the application's ``configure.zcml``, adding the following
stanza:

.. code-block:: xml
   :linenos:

   <notfound
     view="repoze.bfg.views.append_slash_notfound_view"
    />

See :ref:`view_module` and :ref:`changing_the_notfound_view` for more
information about the slash-appending not found view and for a more
general description of how to configure a not found view.

.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.

Cleaning Up After a Request
---------------------------

Often it's required that some cleanup be performed at the end of a
request when a database connection is involved.  When
:term:`traversal` is used, this cleanup is often done as a side effect
of the traversal :term:`root factory`.  Often the root factory will
insert an object into the WSGI environment that performs some cleanup
when its ``__del__`` method is called.  When URL dispatch is used,
however, no special root factory is required, so sometimes that option
is not open to you.

Instead of putting this cleanup logic in the root factory, however,
you can cause a subscriber to be fired when a new request is detected;
the subscriber can do this work.  For example, let's say you have a
``mypackage`` :mod:`repoze.bfg` application package that uses
SQLAlchemy, and you'd like the current SQLAlchemy database session to
be removed after each request.  Put the following in the
``mypackage.run`` module:

.. ignore-next-block
.. code-block:: python
   :linenos:

    from mypackage.sql import DBSession

    class Cleanup:
        def __init__(self, cleaner):
            self.cleaner = cleaner
        def __del__(self):
            self.cleaner()

    def handle_teardown(event):
        environ = event.request.environ
        environ['mypackage.sqlcleaner'] = Cleanup(DBSession.remove)

Then in the ``configure.zcml`` of your package, inject the following:

.. code-block:: xml

  <subscriber for="repoze.bfg.interfaces.INewRequest"
    handler="mypackage.run.handle_teardown"/>

This will cause the DBSession to be removed whenever the WSGI
environment is destroyed (usually at the end of every request).

Alternate mechanisms for performing this sort of cleanup exist; an
alternate mechanism which uses cleanup services offered by the
``repoze.tm2`` package is used in the SQLAlchemy-related ``paster``
templates generated by :mod:`repoze.bfg` and within
:ref:`sql_tm2_cleanup` within the :ref:`bfg_sql_wiki_tutorial`.

.. index::
   pair: URL dispatch; security

.. _using_security_with_urldispatch:

Using :mod:`repoze.bfg` Security With URL Dispatch
--------------------------------------------------

:mod:`repoze.bfg` provides its own security framework which consults a
:term:`authorization policy` before allowing any application code to
be called.  This framework operates in terms of an access control
list, which is stored as an ``__acl__`` attribute of a context object.
A common thing to want to do is to attach an ``__acl__`` to the
context object dynamically for declarative security purposes.  You can
use the ``factory`` argument that points at a factory which attaches a
custom ``__acl__`` to an object at its creation time.

Such a ``factory`` might look like so:

.. code-block:: python
   :linenos:

   class Article(object):
       def __init__(self, request):
          matchdict = request.matchdict
          article = matchdict.get('article', None)
          if article == '1':
              self.__acl__ = [ (Allow, 'editor', 'view') ]

If the route ``archives/:article`` is matched, and the article number
is ``1``, :mod:`repoze.bfg` will generate an ``Article``
:term:`context` with an ACL on it that allows the ``editor`` principal
the ``view`` permission.  Obviously you can do more generic things
that inspect the routes match dict to see if the ``article`` argument
matches a particular string; our sample ``Article`` factory class is
not very ambitious.

.. note:: See :ref:`security_chapter` for more information about
   :mod:`repoze.bfg` security and ACLs.

References
----------

A tutorial showing how :term:`URL dispatch` can be used to create a
:mod:`repoze.bfg` application exists in :ref:`bfg_sql_wiki_tutorial`.