Tuesday, September 22, 2009

Twisted Web in 60 seconds: error handling


Welcome to the fifth installment of "Twisted Web in 60 seconds". In the previous installment, I demonstrated how a Twisted Web server can decide how to respond to requests based on dynamic inspection of the request URL. In this installment, I'll show you how to extend such dynamic dispatch to return a 404 (not found) response when a client requests a non-existent URL.




As in the previous installments, we'll start with Site, Resource, and reactor imports (see the first and second installments for explanations of these):

  from twisted.web.server import Site
 from twisted.web.resource import Resource
 from twisted.internet import reactor




Next, we'll add one more import. NoResource is one of the pre-defined error resources provided by Twisted Web. It generates the necessary 404 response code and renders a simple html page telling the client there is no such resource.

  from twisted.web.error import NoResource




Next, we'll define a custom resource which does some dynamic URL dispatch. This example is going to be just like the previous one, where the path segment is interpreted as a year; the difference is that this time, we'll handle requests which don't conform to that pattern by returning the not found response:

  class Calendar(Resource):
     def getChild(self, name, request):
         try:
             year = int(name)
         except ValueError:
             return NoResource()
         else:
             return YearPage(year)




Aside from including the definition of YearPage from the previous installment, the only other thing left to do is the normal Site and reactor setup. Here's the complete code for this example:

from twisted.web.server import Site
from twisted.web.resource import Resource
from twisted.internet import reactor
from twisted.web.error import NoResource

from calendar import calendar

class YearPage(Resource):
   def __init__(self, year):
       Resource.__init__(self)
       self.year = year

   def render_GET(self, request):
       return "<html><body><pre>%s</pre></body></html>" % (calendar(self.year),)

class Calendar(Resource):
   def getChild(self, name, request):
       try:
           year = int(name)
       except ValueError:
           return NoResource()
       else:
           return YearPage(year)

root = Calendar()
factory = Site(root)
reactor.listenTCP(8880, factory)
reactor.run()




This server hands out the same calendar views as the one from the previous installment, but it will also hand out a nice error page with a 404 response when a request is made for a URL which cannot be interpreted as a year.




Next time I'll show you how you can define resources like NoResource yourself.

3 comments:

  1. Hello,

    thank you for the great work and the twisted framework. I occasionally tried twisted with good results but sometimes stuck at some points. Now in my last year of study i try to completly switch to twisted (python) for my future work. Your blog helps a lot doing this.

    Thanks

    ReplyDelete
  2. First off, thanks for this series. I've been getting involved with twisted and just found your blog. I'm catching up on the articles and it's been filling in a bunch of little gaps in my knowledge, so many thanks.

    A couple suggestions for other related topics to blog on:

    * Understanding deferreds
    * Authentication
    * Perspective Broker

    In any case, on to my question. I'm noticing a trend with your tutorials, as well as other folks examples that the general approach seems to be to create one Resource for the logic on deciding what page to hand out, and yet a different resource for each actual page (or related pages). Is this the "common" approach? Why not incorporate the render_* methods into the logic Resource?

    Thanks again for the series.

    ReplyDelete
  3. I liked the information you contained in it. I will bookmark your site to check if you write more about in the future. Many thanks! Thanks for sharing this site.

    ReplyDelete