1 .. _guide.handlers: 2 3 Request handlers 4 ================ 5 In the webapp2 vocabulary, `request handler` or simply `handler` is a common 6 term that refers to the callable that contains the application logic to handle 7 a request. This sounds a lot abstract, but we will explain everything in 8 details in this section. 9 10 11 Handlers 101 12 ------------ 13 A handler is equivalent to the `Controller` in the 14 `MVC <http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller>`_ 15 terminology: in a simplified manner, it is where you process the request, 16 manipulate data and define a response to be returned to the client: HTML, 17 JSON, XML, files or whatever the app requires. 18 19 Normally a handler is a class that extends :class:`webapp2.RequestHandler` 20 or, for compatibility purposes, ``webapp.RequestHandler``. Here is a simple 21 one:: 22 23 class ProductHandler(webapp2.RequestHandler): 24 def get(self, product_id): 25 self.response.write('You requested product %r.' % product_id) 26 27 app = webapp2.WSGIApplication([ 28 (r'/products/(\d+)', ProductHandler), 29 ]) 30 31 This code defines one request handler, ``ProductHandler``, and a WSGI 32 application that maps the URI ``r'/products/(\d+)'`` to that handler. 33 When the application receives an HTTP request to a path that matches this 34 regular expression, it instantiates the handler and calls the corresponding 35 HTTP method from it. The handler above can only handle ``GET`` HTTP requests, 36 as it only defines a ``get()`` method. To handle ``POST`` requests, 37 it would need to implement a ``post()`` method, and so on. 38 39 The handler method receives a ``product_id`` extracted from the URI, and 40 sets a simple message containing the id as response. Not very useful, but this 41 is just to show how it works. In a more complete example, the handler would 42 fetch a corresponding record from a database and set an appropriate response 43 -- HTML, JSON or XML with details about the requested product, for example. 44 45 For more details about how URI variables are defined, see :ref:`guide.routing`. 46 47 48 HTTP methods translated to class methods 49 ---------------------------------------- 50 The default behavior of the :class:`webapp2.RequestHandler` is to call a 51 method that corresponds with the HTTP action of the request, such as the 52 ``get()`` method for a HTTP GET request. The method processes the request and 53 prepares a response, then returns. Finally, the application sends the response 54 to the client. 55 56 The following example defines a request handler that responds to HTTP GET 57 requests:: 58 59 class AddTwoNumbers(webapp2.RequestHandler): 60 def get(self): 61 try: 62 first = int(self.request.get('first')) 63 second = int(self.request.get('second')) 64 65 self.response.write("<html><body><p>%d + %d = %d</p></body></html>" % 66 (first, second, first + second)) 67 except (TypeError, ValueError): 68 self.response.write("<html><body><p>Invalid inputs</p></body></html>") 69 70 A request handler can define any of the following methods to handle the 71 corresponding HTTP actions: 72 73 - ``get()`` 74 - ``post()`` 75 - ``head()`` 76 - ``options()`` 77 - ``put()`` 78 - ``delete()`` 79 - ``trace()`` 80 81 82 View functions 83 -------------- 84 In some Python frameworks, handlers are called `view functions` or simply 85 `views`. In Django, for example, `views` are normally simple functions that 86 handle a request. Our examples use mostly classes, but webapp2 handlers can 87 also be normal functions equivalent to Django's `views`. 88 89 A webapp2 handler can, really, be **any** callable. The routing system has 90 hooks to adapt how handlers are called, and two default adapters are used 91 whether it is a function or a class. So, differently from webapp, ordinary 92 functions can easily be used to handle requests in webapp2, and not only 93 classes. The following example demonstrates it:: 94 95 def display_product(request, *args, **kwargs): 96 return webapp2.Response('You requested product %r.' % args[0]) 97 98 app = webapp2.WSGIApplication([ 99 (r'/products/(\d+)', display_product), 100 ]) 101 102 Here, our handler is a simple function that receives the request instance, 103 positional route variables as ``*args`` and named variables as ``**kwargs``, 104 if they are defined. 105 106 Apps can have mixed handler classes and functions. Also it is possible to 107 implement new interfaces to define how handlers are called: this is done 108 setting new handler adapters in the routing system. 109 110 Functions are an alternative for those that prefer their simplicity or think 111 that handlers don't benefit that much from the power and flexibility provided 112 by classes: inheritance, attributes, grouped methods, descriptors, metaclasses, 113 etc. An app can have mixed handler classes and functions. 114 115 .. note:: 116 We avoid using the term `view` because it is often confused with the `View` 117 definition from the classic `MVC` pattern. Django prefers to call its `MVC` 118 implementation `MTV` (model-template-view), so `view` may make sense in 119 their terminology. Still, we think that the term can cause unnecessary 120 confusion and prefer to use `handler` instead, like in other Python 121 frameworks (webapp, web.py or Tornado, for instance). In essence, though, 122 they are synonyms. 123 124 125 .. _guide.handlers.returned_values: 126 127 Returned values 128 --------------- 129 A handler method doesn't need to return anything: it can simply write to the 130 response object using ``self.response.write()``. 131 132 But a handler **can** return values to be used in the response. Using the 133 default dispatcher implementation, if a handler returns anything that is not 134 ``None`` it **must** be a :class:`webapp2.Response` instance. If it does so, 135 that response object is used instead of the default one. 136 137 For example, let's return a response object with a `Hello, world` message:: 138 139 class HelloHandler(webapp2.RequestHandler): 140 def get(self): 141 return webapp2.Response('Hello, world!') 142 143 This is the same as:: 144 145 class HelloHandler(webapp2.RequestHandler): 146 def get(self): 147 self.response.write('Hello, world!') 148 149 What if you think that returning a response object is verbose, and want to 150 return simple strings? Fortunately webapp2 has all the necessary hooks to make 151 this possible. To achieve it, we need to extend the router dispatcher to build 152 a ``Response`` object using the returned string. We can go even further and 153 also accept tuples: if a tuple is returned, we use its values as positional 154 arguments to instantiate the ``Response`` object. So let's define our custom 155 dispatcher and a handler that returns a string:: 156 157 def custom_dispatcher(router, request, response): 158 rv = router.default_dispatcher(request, response) 159 if isinstance(rv, basestring): 160 rv = webapp2.Response(rv) 161 elif isinstance(rv, tuple): 162 rv = webapp2.Response(*rv) 163 164 return rv 165 166 class HelloHandler(webapp2.RequestHandler): 167 def get(self, *args, **kwargs): 168 return 'Hello, world!' 169 170 app = webapp2.WSGIApplication([ 171 (r'/', HelloHandler), 172 ]) 173 app.router.set_dispatcher(custom_dispatcher) 174 175 And that's all. Now we have a custom dispatcher set using the router method 176 :meth:`webapp2.Router.set_dispatcher`. Our ``HelloHandler`` returns a string 177 (or it could be tuple) that is used to create a ``Response`` object. 178 179 Our custom dispatcher could implement its own URI matching and handler 180 dispatching mechanisms from scratch, but in this case it just extends the 181 default dispatcher a little bit, wrapping the returned value under certain 182 conditions. 183 184 .. _guide.handlers.a.micro.framework.based.on.webapp2: 185 186 A micro-framework based on webapp2 187 ---------------------------------- 188 Following the previous idea of a custom dispatcher, we could go a little 189 further and extend webapp2 to accept routes registered using a decorator, 190 like in those Python micro-frameworks. 191 192 Without much ado, ladies and gentlemen, we present micro-webapp2:: 193 194 import webapp2 195 196 class WSGIApplication(webapp2.WSGIApplication): 197 def __init__(self, *args, **kwargs): 198 super(WSGIApplication, self).__init__(*args, **kwargs) 199 self.router.set_dispatcher(self.__class__.custom_dispatcher) 200 201 @staticmethod 202 def custom_dispatcher(router, request, response): 203 rv = router.default_dispatcher(request, response) 204 if isinstance(rv, basestring): 205 rv = webapp2.Response(rv) 206 elif isinstance(rv, tuple): 207 rv = webapp2.Response(*rv) 208 209 return rv 210 211 def route(self, *args, **kwargs): 212 def wrapper(func): 213 self.router.add(webapp2.Route(handler=func, *args, **kwargs)) 214 return func 215 216 return wrapper 217 218 Save the above code as ``micro_webapp2.py``. Then you can import it in 219 ``main.py`` and define your handlers and routes like this:: 220 221 import micro_webapp2 222 223 app = micro_webapp2.WSGIApplication() 224 225 @app.route('/') 226 def hello_handler(request, *args, **kwargs): 227 return 'Hello, world!' 228 229 def main(): 230 app.run() 231 232 if __name__ == '__main__': 233 main() 234 235 This example just demonstrates some of the power and flexibility that lies 236 behind webapp2; explore the :ref:`webapp2 API <api.webapp2>` to discover other 237 ways to modify or extend the application behavior. 238 239 240 Overriding __init__() 241 --------------------- 242 If you want to override the :meth:`webapp2.RequestHandler.__init__` method, 243 you must call :meth:`webapp2.RequestHandler.initialize` at the beginning of 244 the method. It'll set the current request, response and app objects as 245 attributes of the handler. For example:: 246 247 class MyHandler(webapp2.RequestHandler): 248 def __init__(self, request, response): 249 # Set self.request, self.response and self.app. 250 self.initialize(request, response) 251 252 # ... add your custom initializations here ... 253 # ... 254 255 256 Overriding dispatch() 257 --------------------- 258 One of the advantadges of webapp2 over webapp is that you can wrap the 259 dispatching process of :class:`webapp2.RequestHandler` to perform actions 260 before and/or after the requested method is dispatched. You can do this 261 overriding the :meth:`webapp2.RequestHandler.dispatch` method. This can be 262 useful, for example, to test if requirements were met before actually 263 dispatching the requested method, or to perform actions in the response object 264 after the method was dispatched. Here's an example:: 265 266 class MyHandler(webapp2.RequestHandler): 267 def dispatch(self): 268 # ... check if requirements were met ... 269 # ... 270 271 if requirements_were_met: 272 # Parent class will call the method to be dispatched 273 # -- get() or post() or etc. 274 super(MyHandler, self).dispatch() 275 else: 276 self.abort(403) 277 278 In this case, if the requirements were not met, the method won't ever be 279 dispatched and a "403 Forbidden" response will be returned instead. 280 281 There are several possibilities to explore overriding ``dispatch()``, like 282 performing common checkings, setting common attributes or post-processing the 283 response. 284