In the webapp2 vocabulary, request handler or simply handler is a common term that refers to the callable that contains the application logic to handle a request. This sounds a lot abstract, but we will explain everything in details in this section.
A handler is equivalent to the Controller in the MVC terminology: in a simplified manner, it is where you process the request, manipulate data and define a response to be returned to the client: HTML, JSON, XML, files or whatever the app requires.
Normally a handler is a class that extends webapp2.RequestHandler or, for compatibility purposes, webapp.RequestHandler. Here is a simple one:
class ProductHandler(webapp2.RequestHandler): def get(self, product_id): self.response.write('You requested product %r.' % product_id) app = webapp2.WSGIApplication([ (r'/products/(\d+)', ProductHandler), ])
This code defines one request handler, ProductHandler, and a WSGI application that maps the URI r'/products/(\d+)' to that handler. When the application receives an HTTP request to a path that matches this regular expression, it instantiates the handler and calls the corresponding HTTP method from it. The handler above can only handle GET HTTP requests, as it only defines a get() method. To handle POST requests, it would need to implement a post() method, and so on.
The handler method receives a product_id extracted from the URI, and sets a simple message containing the id as response. Not very useful, but this is just to show how it works. In a more complete example, the handler would fetch a corresponding record from a database and set an appropriate response – HTML, JSON or XML with details about the requested product, for example.
For more details about how URI variables are defined, see URI routing.
The default behavior of the webapp2.RequestHandler is to call a method that corresponds with the HTTP action of the request, such as the get() method for a HTTP GET request. The method processes the request and prepares a response, then returns. Finally, the application sends the response to the client.
The following example defines a request handler that responds to HTTP GET requests:
class AddTwoNumbers(webapp2.RequestHandler): def get(self): try: first = int(self.request.get('first')) second = int(self.request.get('second')) self.response.write("<html><body><p>%d + %d = %d</p></body></html>" % (first, second, first + second)) except (TypeError, ValueError): self.response.write("<html><body><p>Invalid inputs</p></body></html>")
A request handler can define any of the following methods to handle the corresponding HTTP actions:
In some Python frameworks, handlers are called view functions or simply views. In Django, for example, views are normally simple functions that handle a request. Our examples use mostly classes, but webapp2 handlers can also be normal functions equivalent to Django’s views.
A webapp2 handler can, really, be any callable. The routing system has hooks to adapt how handlers are called, and two default adapters are used whether it is a function or a class. So, differently from webapp, ordinary functions can easily be used to handle requests in webapp2, and not only classes. The following example demonstrates it:
def display_product(request, *args, **kwargs): return webapp2.Response('You requested product %r.' % args) app = webapp2.WSGIApplication([ (r'/products/(\d+)', display_product), ])
Here, our handler is a simple function that receives the request instance, positional route variables as *args and named variables as **kwargs, if they are defined.
Apps can have mixed handler classes and functions. Also it is possible to implement new interfaces to define how handlers are called: this is done setting new handler adapters in the routing system.
Functions are an alternative for those that prefer their simplicity or think that handlers don’t benefit that much from the power and flexibility provided by classes: inheritance, attributes, grouped methods, descriptors, metaclasses, etc. An app can have mixed handler classes and functions.
We avoid using the term view because it is often confused with the View definition from the classic MVC pattern. Django prefers to call its MVC implementation MTV (model-template-view), so view may make sense in their terminology. Still, we think that the term can cause unnecessary confusion and prefer to use handler instead, like in other Python frameworks (webapp, web.py or Tornado, for instance). In essence, though, they are synonyms.
A handler method doesn’t need to return anything: it can simply write to the response object using self.response.write().
But a handler can return values to be used in the response. Using the default dispatcher implementation, if a handler returns anything that is not None it must be a webapp2.Response instance. If it does so, that response object is used instead of the default one.
For example, let’s return a response object with a Hello, world message:
class HelloHandler(webapp2.RequestHandler): def get(self): return webapp2.Response('Hello, world!')
This is the same as:
class HelloHandler(webapp2.RequestHandler): def get(self): self.response.write('Hello, world!')
What if you think that returning a response object is verbose, and want to return simple strings? Fortunately webapp2 has all the necessary hooks to make this possible. To achieve it, we need to extend the router dispatcher to build a Response object using the returned string. We can go even further and also accept tuples: if a tuple is returned, we use its values as positional arguments to instantiate the Response object. So let’s define our custom dispatcher and a handler that returns a string:
def custom_dispatcher(router, request, response): rv = router.default_dispatcher(request, response) if isinstance(rv, basestring): rv = webapp2.Response(rv) elif isinstance(rv, tuple): rv = webapp2.Response(*rv) return rv class HelloHandler(webapp2.RequestHandler): def get(self, *args, **kwargs): return 'Hello, world!' app = webapp2.WSGIApplication([ (r'/', HelloHandler), ]) app.router.set_dispatcher(custom_dispatcher)
And that’s all. Now we have a custom dispatcher set using the router method webapp2.Router.set_dispatcher(). Our HelloHandler returns a string (or it could be tuple) that is used to create a Response object.
Our custom dispatcher could implement its own URI matching and handler dispatching mechanisms from scratch, but in this case it just extends the default dispatcher a little bit, wrapping the returned value under certain conditions.
Following the previous idea of a custom dispatcher, we could go a little further and extend webapp2 to accept routes registered using a decorator, like in those Python micro-frameworks.
Without much ado, ladies and gentlemen, we present micro-webapp2:
import webapp2 class WSGIApplication(webapp2.WSGIApplication): def __init__(self, *args, **kwargs): super(WSGIApplication, self).__init__(*args, **kwargs) self.router.set_dispatcher(self.__class__.custom_dispatcher) @staticmethod def custom_dispatcher(router, request, response): rv = router.default_dispatcher(request, response) if isinstance(rv, basestring): rv = webapp2.Response(rv) elif isinstance(rv, tuple): rv = webapp2.Response(*rv) return rv def route(self, *args, **kwargs): def wrapper(func): self.router.add(webapp2.Route(handler=func, *args, **kwargs)) return func return wrapper
Save the above code as micro_webapp2.py. Then you can import it in main.py and define your handlers and routes like this:
import micro_webapp2 app = micro_webapp2.WSGIApplication() @app.route('/') def hello_handler(request, *args, **kwargs): return 'Hello, world!' def main(): app.run() if __name__ == '__main__': main()
This example just demonstrates some of the power and flexibility that lies behind webapp2; explore the webapp2 API to discover other ways to modify or extend the application behavior.
If you want to override the webapp2.RequestHandler.__init__() method, you must call webapp2.RequestHandler.initialize() at the beginning of the method. It’ll set the current request, response and app objects as attributes of the handler. For example:
class MyHandler(webapp2.RequestHandler): def __init__(self, request, response): # Set self.request, self.response and self.app. self.initialize(request, response) # ... add your custom initializations here ... # ...
One of the advantadges of webapp2 over webapp is that you can wrap the dispatching process of webapp2.RequestHandler to perform actions before and/or after the requested method is dispatched. You can do this overriding the webapp2.RequestHandler.dispatch() method. This can be useful, for example, to test if requirements were met before actually dispatching the requested method, or to perform actions in the response object after the method was dispatched. Here’s an example:
class MyHandler(webapp2.RequestHandler): def dispatch(self): # ... check if requirements were met ... # ... if requirements_were_met: # Parent class will call the method to be dispatched # -- get() or post() or etc. super(MyHandler, self).dispatch() else: self.abort(403)
In this case, if the requirements were not met, the method won’t ever be dispatched and a “403 Forbidden” response will be returned instead.
There are several possibilities to explore overriding dispatch(), like performing common checkings, setting common attributes or post-processing the response.