WebStack

docs/path-strategies.html

733:26865b172666
2007-11-12 paulb [project @ 2007-11-12 00:51:34 by paulb] Added a StringResource class for simple static resources. Introduced base classes for common authentication activities. Merged "app", "path" and "qs" fields into a single "app" field for login and redirection. Added support for OpenID authentication.
     1 <?xml version="1.0" encoding="iso-8859-1"?>     2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">     3 <html xmlns="http://www.w3.org/1999/xhtml"><head>     4   <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type" /><title>Path Processing Strategies</title>     5   <link href="styles.css" rel="stylesheet" type="text/css" /></head>     6 <body><h1>Path Processing Strategies</h1>     7 <p>In the development of most Web applications, the structure of the     8 application - also known as the "site map" - needs to be defined     9 at a very early stage. We might decide that if a user requests a    10 certain path, a particular part of the application will be invoked, and    11 we might define the site map in a number of different ways:</p><p>As a set of acceptable paths...</p><ul><li><code>/</code> is the main page</li><li><code>/services/finance/salaries/</code> is the salary report page</li><li><code>/services/customer/complaints/</code> is the complaints page</li></ul><p>As a tree of resources...</p><ul><li><code>/</code> is the main page</li><ul><li><code>.../services/</code> refers to services (which may not be defined as anything viewable)</li><ul><li><code>.../finance/</code> is the finance department</li><ul><li><code>.../salaries/</code> is the salary report page</li></ul><li><code>.../customer/</code> is the customer service department</li><ul><li><code>.../complaints/</code> is the complaints page</li></ul></ul></ul></ul><p>Since    12 all of the action in WebStack applications takes place inside    13 resources, the challenge is to define resources in such a way which    14 makes processing paths relatively easy.</p><h2>Chaining Resources</h2><p>Whilst the classic resource, as described in <a href="resources.html">"Applications and Resources"</a>, might resemble a simple class whose <code>respond</code>    15 method performs most of the necessary work, it is useful to reconsider    16 such a resource as doing such work only for a particular narrow part of    17 a larger Web application. Moreover, resources are not restricted in    18 their usage of other objects to carry out their purpose, provided they    19 are initialised with references to those objects.Consequently, it makes sense to consider defining a resource which, if it alone cannot process a request, invokes the  <code>respond</code> method on another resource in order to get that resource to continue with the act of processing.</p><p>We    20 can apply this insight to the above path processing scenario. If we    21 first employ a resource to examine details of the path, and if that    22 resource then invokes other resources to produce certain pages, we can    23 separate the path processing from the rest of the application's    24 functionality. So, for the first site map strategy, we could define a    25 path processing resource as follows:</p><pre>from WebStack.Generic import EndOfResponse<br /><br />class PathProcessor:<br /><br />    "A path processing resource."<br /><br />    def __init__(self, main_page, salary_report_page, complaints_page):<br /><br />        # Supplied resources are chained to this resource.<br /><br />        self.main_page = main_page<br />        self.salary_report_page = salary_report_page<br />        self.complaints_page = complaints_page<br /><br />    def respond(self, trans):<br /><br />        # This is where the resources are invoked...<br /><br />        path = trans.get_path_without_query() # should really use an encoding here<br />        if path == "/":<br />            self.main_page.respond(trans)<br />        elif path == "/services/finance/salaries/":<br />            self.main_page.respond(trans)<br />        elif path == "/services/finance/salaries/":<br />            self.main_page.respond(trans)<br /><br />        # Administrative details...<br /><br />        else:<br />            trans.set_response_code(404) # not found!<br />            raise EndOfResponse</pre><p>Of    26 course, more elegant methods of mapping paths to resources could be    27 employed - a dictionary might be an acceptable solution, if defined in    28 the initialiser method. The above class might be initialised as follows:</p><pre>main_page = MainResource()<br />salary_report_page = SalaryReportResource()<br />complaints_page = ComplaintsResource()<br />path_processor = PathProcessor(main_page, salary_report_page, complaints_page)</pre><p>For    29 the second site map strategy, we retain the basic parts of the above    30 strategy, focusing only on one level in the path as opposed to the    31 complete path. However, this means that our path processing resources    32 need to know exactly which part of the path they should be    33 considering, and perhaps which part of the path has already been    34 processed.</p><p>As described in <a href="path-info.html">"Paths To and Within Applications"</a>, special path information of the nature required is provided by two methods: <code>get_virtual_path_info</code> and <code>get_processed_virtual_path_info</code>. Such information can thus be obtained and updated at each level in the processing chain.</p><pre>class MainPathProcessor:<br /><br />    "A path processor for the top of the application."<br /><br />    def __init__(self, main_page, services_processor):<br />        self.main_page = main_page<br />        self.services_processor = services_processor<br /><br />    def respond(self, trans):<br /><br />        # This is where the resources are invoked...<br /><br />        path = trans.get_virtual_path_info() # should really use an encoding here<br />        if path == "/":<br />            self.main_page.respond(trans)<br />        elif path.startswith("/services/"):<br />            trans.set_virtual_path_info(path[len("/services"):]) # cut off "/services"<br />            self.services_processor.respond(trans)<br /><br />        # Administrative details...<br /><br />        else:<br />            trans.set_response_code(404) # not found!<br />            raise EndOfResponse</pre><p>A suite of similar classes can be defined and might be initialised as follows:</p><pre>main_page = MainResource()<br />salary_report_page = SalaryReportResource()<br />complaints_page = ComplaintsResource()<br />finance_processor = FinancePathProcessor(salary_report_page)<br />customer_processor = CustomerPathProcessor(complaints_page)<br />services_processor = ServicesPathProcessor(finance_processor, customer_processor)<br />main_processor = MainPathProcessor(main_page, services_processor)</pre><p>Fortunately, this latter strategy is supported by WebStack in the form of the <code>ResourceMap</code> module. See <a href="paths-filesystem.html">"Treating the Path Like a Filesystem"</a> and <a href="resource-map.html">"ResourceMap - Simple Mappings from Names to Resources"</a> for details.</p></body></html>