1.. _features: 2 3webapp2 features 4================ 5Here's an overview of the main improvements of webapp2 compared to webapp. 6 7.. contents:: Table of Contents 8 :depth: 3 9 :backlinks: none 10 11 12Compatible with webapp 13---------------------- 14webapp2 is designed to work with existing webapp apps without any changes. 15See how this looks familiar:: 16 17 import webapp2 as webapp 18 from google.appengine.ext.webapp.util import run_wsgi_app 19 20 class HelloWorldHandler(webapp.RequestHandler): 21 def get(self): 22 self.response.out.write('Hello, World!') 23 24 app = webapp.WSGIApplication([ 25 ('/', HelloWorldHandler), 26 ], debug=True) 27 28 def main(): 29 run_wsgi_app(app) 30 31 if __name__ == '__main__': 32 main() 33 34Everybody starting with App Engine must know a bit of webapp. And you you can 35use webapp2 exactly like webapp, following the official tutorials, and learn 36the new features later, as you go. This makes webapp2 insanely easy to learn. 37 38Also, the SDK libraries that use webapp can be used with webapp2 as they are 39or with minimal adaptations. 40 41 42Compatible with latest WebOb 43---------------------------- 44The ``WebOb`` version included in the App Engine SDK was released in 2008. 45Since then many bugs were fixed and the source code became cleaner and better 46documented. webapp2 is compatible with the ``WebOb`` version included in the 47SDK, but for those that prefer the latest version can be used as well. 48This avoids the bugs 49`#170 <http://code.google.com/p/googleappengine/issues/detail?id=170>`_, 50`#719 <http://code.google.com/p/googleappengine/issues/detail?id=719>`_ and 51`#2788 <http://code.google.com/p/googleappengine/issues/detail?id=2788>`_, 52at least. 53 54 55Full-featured response object 56----------------------------- 57webapp2 uses a full-featured response object from ``WebOb``. If offers several 58conveniences to set headers, like easy cookies and other goodies:: 59 60 class MyHandler(webapp2.RequestHandler): 61 def get(self): 62 self.response.set_cookie('key', 'value', max_age=360, path='/') 63 64 65Status code exceptions 66---------------------- 67``abort()`` (or ``self.abort()`` inside handlers) raises a proper 68``HTTPException`` (from ``WebOb``) and stops processing:: 69 70 # Raise a 'Not Found' exception and let the 404 error handler do its job. 71 abort(404) 72 # Raise a 'Forbidden' exception and let the 403 error handler do its job. 73 self.abort(403) 74 75 76Improved exception handling 77--------------------------- 78HTTP exceptions can also be handled by the WSGI application:: 79 80 # ... 81 import logging 82 83 def handle_404(request, response, exception): 84 logging.exception(exception) 85 response.write('Oops! I could swear this page was here!') 86 response.set_status(404) 87 88 app = webapp2.WSGIApplication([ 89 ('/', MyHandler), 90 ]) 91 app.error_handlers[404] = handle_404 92 93 94Lazy handlers 95------------- 96Lazy handlers can be defined as a string to be imported only when needed:: 97 98 app = webapp2.WSGIApplication([ 99 ('/', 'my.module.MyHandler'), 100 ]) 101 102 103Keyword arguments from URI 104-------------------------- 105``RequestHandler`` methods can also receive keyword arguments, which are easier 106to maintain than positional ones. Simply use the ``Route`` class to define 107URIs (and you can also create custom route classes, examples 108`here <http://code.google.com/p/webapp-improved/source/browse/webapp2_extras/routes.py>`_):: 109 110 class BlogArchiveHandler(webapp2.RequestHandler): 111 def get(self, year=None, month=None): 112 self.response.write('Hello, keyword arguments world!') 113 114 app = webapp2.WSGIApplication([ 115 webapp2.Route('/<year:\d{4}>/<month:\d{2}>', handler=BlogArchiveHandler, name='blog-archive'), 116 ]) 117 118 119Positional arguments from URI 120----------------------------- 121Positional arguments are also supported, as URI routing is fully compatible 122with webapp:: 123 124 class BlogArchiveHandler(webapp2.RequestHandler): 125 def get(self, year, month): 126 self.response.write('Hello, webapp routing world!') 127 128 app = webapp2.WSGIApplication([ 129 ('/(\d{4})/(\d{2})', BlogArchiveHandler), 130 ]) 131 132 133Returned responses 134------------------ 135Several Python frameworks adopt the pattern on returning a response object, 136instead of writing to an existing response object like webapp. For those that 137prefer, webapp2 supports this: simply return a response object from a handler 138and it will be used instead of the one created by the application:: 139 140 class BlogArchiveHandler(webapp2.RequestHandler): 141 def get(self): 142 return webapp2.Response('Hello, returned response world!') 143 144 app = webapp2.WSGIApplication([ 145 webapp2.Route('/', handler=HomeHandler, name='home'), 146 ]) 147 148 149Custom handler methods 150---------------------- 151webapp2 routing and dispatching system can do a lot more than webapp. 152For example, handlers can also use custom methods:: 153 154 class MyHandler(webapp2.RequestHandler): 155 def my_custom_method(self): 156 self.response.write('Hello, custom method world!') 157 158 def my_other_method(self): 159 self.response.write('Hello, another custom method world!') 160 161 app = webapp2.WSGIApplication([ 162 webapp2.Route('/', handler=MyHandler, name='custom-1', handler_method='my_custom_method'), 163 webapp2.Route('/other', handler=MyHandler, name='custom-2', handler_method='my_other_method'), 164 ]) 165 166 167View functions 168-------------- 169In webapp2 handlers don't need necessarily to be classes. For those that 170prefer, functions can be used as well:: 171 172 def my_sweet_function(request, *args, **kwargs): 173 return webapp2.Response('Hello, function world!') 174 175 app = webapp2.WSGIApplication([ 176 webapp2.Route('/', handler=my_sweet_function, name='home'), 177 ]) 178 179 180More flexible dispatching mechanism 181----------------------------------- 182The ``WSGIApplication`` in webapp is hard to modify. It dispatches the 183handler giving little chance to define how it is done, or to pre-process 184requests before a handler method is actually called. In webapp2 the handlers 185dispatch themselves, making it easy to implement before and after dispatch 186hooks. 187 188webapp2 is thought to be lightweight but flexible. It basically provides an 189easy to customize URI routing and dispatching mechanisms: you can even extend 190how URIs are matched or built or how handlers are adapted or dispatched 191without subclassing. 192 193For an example of webapp2's flexibility, 194see :ref:`guide.handlers.a.micro.framework.based.on.webapp2`. 195 196 197Domain and subdomain routing 198---------------------------- 199webapp2 supports :ref:`domain and subdomain routing <guide.routing.domain-and-subdomain-routing>` 200to restrict URI matches based on the server name:: 201 202 routes.DomainRoute('www.mydomain.com', [ 203 webapp2.Route('/', handler=HomeHandler, name='home'), 204 ]) 205 206 207Match HTTP methods or URI schemes 208--------------------------------- 209webapp2 routing system allows routes to be restricted to the 210:ref:`HTTP method <guide.routing.restricting-http-methods>` or a specific 211:ref:`URI scheme <guide.routing.restricting-uri-schemes>`. You can set routes 212that will only match requests using 'https', for example. 213 214 215URI builder 216----------- 217URIs defined in the aplication can be built. This is more maintanable than 218hardcoding them in the code or templates. Simply use the ``uri_for()`` 219function:: 220 221 uri = uri_for('blog-archive', year='2010', month='07') 222 223And a handler helper for redirects builds the URI to redirect to. 224redirect_to = redirect + uri_for:: 225 226 self.redirect_to('blog-archive', year='2010', month='07') 227 228 229Redirection for legacy URIs 230--------------------------- 231Old URIs can be conveniently redirected using a simple route:: 232 233 def get_redirect_uri(handler, *args, **kwargs): 234 return handler.uri_for('view', item=kwargs.get('item')) 235 236 app = webapp2.WSGIApplication([ 237 webapp2.Route('/view/<item>', ViewHandler, 'view'), 238 webapp2.Route('/old-page', RedirectHandler, defaults={'uri': '/view/i-came-from-a-redirect'}), 239 webapp2.Route('/old-view/<item>', RedirectHandler, defaults={'uri': get_redirect_uri}), 240 ]) 241 242 243Simple, well-tested and documented 244---------------------------------- 245webapp2 is `simple <http://code.google.com/p/webapp-improved/source/browse/webapp2.py>`_, 246extensively documented and has almost 100% test coverage. The source code is 247explicit, magic-free and made to be extended. We like less. 248 249 250Independent of the App Engine SDK 251--------------------------------- 252webapp2 doesn't depend on the Google App Engine SDK and 253:ref:`can be used outside of App Engine <tutorials.quickstart.nogae>`. 254If the SDK is not found, it has fallbacks to be used in any server as a 255general purpose web framework. 256 257 258Future proof 259------------ 260Because it works on threaded environments, webapp2 is ready for when 261App Engine introduces threading support in the Python 2.7 runtime. 262 263 264Same performance 265---------------- 266Best of all is that with all these features, there is no loss of performance: 267cold start times are the same as webapp. Here are some logs of a 'Hello World' 268cold start: 269 270.. code-block:: text 271 272 100ms 77cpu_ms 273 143ms 58cpu_ms 274 155ms 77cpu_ms 275 197ms 96cpu_ms 276 106ms 77cpu_ms 277 278 279Extras 280------ 281The `webapp2_extras <http://code.google.com/p/webapp-improved/source/browse/#hg%2Fwebapp2_extras>`_ 282package provides common utilities that integrate well with webapp2: 283 284- Localization and internationalization support 285- Sessions using secure cookies, memcache or datastore 286- Extra route classes -- to match subdomains and other conveniences 287- Support for third party libraries: Jinja2 and Mako 288- Support for threaded environments, so that you can use webapp2 outside of 289 App Engine or in the upcoming App Engine Python 2.7 runtime 290