Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-2.x/docs/book/xml/globaloverviewcherrypy.xml

Revision 798 (checked in by fumanchu, 3 years ago)

Docs: more dispatch discussion.

Line 
1 <?xml version="1.0" encoding="UTF-8"?>
2 <section xmlns:db="http://docbook.org/docbook-ng" xmlns:xi="http://www.w3.org/2001/XInclude"
3          xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xml:id="globaloverviewcherrypy">
4     <title>Global Overview</title>
5     <section>
6         <title>Mapping URI's to handlers</title>
7         <para>CherryPy has lots of fancy features to help you manage HTTP messages. But the most
8         fundamental thing it does is allow you to map URI's to handler functions. It does this in a
9         very straightforward way: the path portion of a URI is heirarchical, so CherryPy uses a
10         parallel heirarchy of objects, starting with <code>cherrypy.root</code>. If your application
11         receives a request for "/admin/user?name=idunno", then CherryPy will try to find the handler:
12         <code>cherrypy.root.admin.user</code>. If it exists, is callable, and has an "exposed = True"
13         attribute, then CherryPy will hand off control to that function. Any URI parameters (like
14         "name=idunno", above) are passed to the handler as keyword arguments.</para>
15         <section>
16             <title>Index methods</title>
17             <para>There are some special cases, however. To what handler should we map a path like
18             "/admin/search/"? Note the trailing slash after "search"—it indicates that our path has
19             three components: "admin", "search", and "". Static webservers interpret this to mean
20             that the <code>search</code> object is a directory, and, since the third component is
21             blank, they use an <code>index.html</code> file if it exists. CherryPy is a dynamic
22             webserver, so it allows you to specify an <code>index</code> method to handle this. In
23             our example, CherryPy will look for a handler at
24             <code>cherrypy.root.admin.search.index</code>. Let's pause and show our example
25             application so far:</para>
26             <example>
27                 <title>Sample application (handler mapping example)</title>
28                 <programlisting>import cherrypy
29
30 class Root:
31     def index(self):
32         return "Hello, world!"
33     index.exposed = True
34
35 class Admin:
36     def user(self, name=""):
37         return "You asked for user '%s'" % name
38     user.exposed = True
39
40 class Search:
41     def index(self):
42         return search_page()
43     index.exposed = True
44
45 cherrypy.root = Root()
46 cherrypy.root.admin = Admin()
47 cherrypy.root.admin.search = Search()</programlisting>
48             </example>
49             <para>So far, we have three exposed handlers:</para>
50             <itemizedlist>
51                 <listitem>
52                     <para><code>root.index</code>. This will be called for the URI's "/" and
53                     "/index".</para>
54                 </listitem>
55                 <listitem>
56                     <para><code>root.admin.user</code>. This will be called for the URI
57                     "/admin/user".</para>
58                 </listitem>
59                 <listitem>
60                     <para><code>root.admin.search.index</code>. This will be called for the URI's
61                     "/admin/search/" and "/admin/search".</para>
62                 </listitem>
63             </itemizedlist>
64             <para>Yes, you read that third line correctly: <code>root.admin.search.index</code> will
65             be called whether or not the URI has a trailing slash. Actually, that isn't quite true;
66             CherryPy will answer a request for "/admin/search" (without the slash) with an HTTP
67             Redirect response. Most browsers will then request "/admin/search/" as the redirection
68             suggests, and <emphasis>then</emphasis> our <code>root.admin.search.index</code> handler
69             will be called. But the final outcome is the same.</para>
70         </section>
71         <section>
72             <title>Positional Parameters</title>
73             <para>Now, let's consider another special case. What if, instead of passing a user name
74             as a parameter, we wish to use a user id as part of the path? What to do with a URI like
75             "/admin/user/8173/schedule"? This is intended to reference the schedule belonging to
76             "user #8173", but we certainly don't want to have a separate function for each user
77             id!</para>
78             <para>CherryPy allows you to map a single handler to multiple URI's with the simple
79             approach of <emphasis>not writing handlers you don't need</emphasis>. If a node in the
80             <code>cherrypy.root</code> tree doesn't have any children, that node will be called for
81             all of its child paths, and CherryPy will pass the leftover path info as positional
82             arguments. In our example, CherryPy will call <code>cherrypy.root.admin.user("8173",
83             "schedule")</code>. Let's rewrite our user method to handle such requests:</para>
84             <example>
85                 <title>A user method which handles positional parameters</title>
86                 <programlisting>class Admin:
87     def user(self, *args):
88         if not args:
89             raise cherrypy.HTTPError(400, "A user id was expected but not supplied.")
90         id = args.pop(0)
91         if args and args[0] == 'schedule':
92             return self.schedule(id)
93         return "You asked for user '%s'" % id
94     user.exposed = True</programlisting>
95             </example>
96             <para>Note that this is different behavior than CherryPy 2.1, which only allowed
97             positional params for methods named "default".</para>
98         </section>
99         <section>
100             <title>Default methods</title>
101             <para>Are you ready for another special case? What handler is called in our example if
102             you request the URI "/not/a/valid/path"? Given the behavior we have described up to this
103             point, you might deduce that the <code>root.index</code> method will end up handling
104             <emphasis>any</emphasis> path that can't be mapped elsewhere. This would mean, in effect,
105             that CherryPy applications with a <code>root.index</code> could never return a "404 Not
106             Found" response!</para>
107             <para>To prevent this, CherryPy doesn't try to call index methods unless they are
108             attached to the last node in the path; in our example, the only index method that might
109             be called would be a <code>root.not.a.valid.path.index</code> method. If you truly want
110             an intermediate index method to receive positional parameters, well, you can't do that.
111             But what you can do is define a <code>default</code> method to do that for you, instead
112             of an <code>index</code> method. If we wanted our <code>cherrypy.root</code> to handle
113             any child path, and receive positional parameters, we could rewrite it like this:</para>
114             <example>
115                 <title>A <code>default</code> method example</title>
116                 <programlisting>class Root:
117     def index(self):
118         return "Hello, world!"
119     index.exposed = True
120    
121     def default(self, *args):
122         return "Extra path info: %s" % repr(args)
123     default.exposed = True</programlisting>
124             </example>
125             <para>This new Root class would handle the URI's "/" and "/index" via the
126             <code>index</code> method, and would handle URI's like "/not/a/valid/path" and
127             "/admin/unknown" via the <code>default</code> method.</para>
128         </section>
129         <section>
130             <title>Traversal examples</title>
131             <para>For those of you who need to see in exactly what order CherryPy will try various
132             handlers, here are some examples, using the application above. We always start by trying
133             to find the longest object path first, and then working backwards until an exposed,
134             callable handler is found:</para>
135             <example>
136                 <title>Traversal examples</title>
137                 <programlisting>"/admin/user/8192/schedule"
138     Trying to reach cherrypy.root.admin.user.8192.schedule.index...
139     cherrypy.root                     exists? Yes.
140             .root.admin               exists? Yes.
141                  .admin.user          exists? Yes.
142                        .user.8192     exists? No.
143                        .user.default  is callable and exposed? No.
144                  .admin.user          is callable and exposed? Yes. Call it.
145
146 "/admin/search/"
147     Trying to reach cherrypy.root.admin.search.index...
148     cherrypy.root                     exists? Yes.
149             .root.admin               exists? Yes.
150                  .admin.search        exists? Yes.
151                        .search.index  exists? Yes. Path exhausted.
152                        .search.index  is callable and exposed? Yes. Call it.
153
154 "/admin/unknown"
155     Trying to reach cherrypy.root.admin.unknown.index...
156     cherrypy.root                     exists? Yes.
157             .root.admin               exists? Yes.
158                  .admin.unknown       exists? No.
159                  .admin.default       is callable and exposed? No.
160             .root.admin               is callable and exposed? No.
161             .root.default             is callable and exposed? Yes. Call it.</programlisting>
162             </example>
163         </section>
164     </section>
165 </section>
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets