1 .. _features: 2 3 webapp2 features 4 ================ 5 Here'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 12 Compatible with webapp 13 ---------------------- 14 webapp2 is designed to work with existing webapp apps without any changes. 15 See 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 34 Everybody starting with App Engine must know a bit of webapp. And you you can 35 use webapp2 exactly like webapp, following the official tutorials, and learn 36 the new features later, as you go. This makes webapp2 insanely easy to learn. 37 38 Also, the SDK libraries that use webapp can be used with webapp2 as they are 39 or with minimal adaptations. 40 41 42 Compatible with latest WebOb 43 ---------------------------- 44 The ``WebOb`` version included in the App Engine SDK was released in 2008. 45 Since then many bugs were fixed and the source code became cleaner and better 46 documented. webapp2 is compatible with the ``WebOb`` version included in the 47 SDK, but for those that prefer the latest version can be used as well. 48 This 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>`_, 52 at least. 53 54 55 Full-featured response object 56 ----------------------------- 57 webapp2 uses a full-featured response object from ``WebOb``. If offers several 58 conveniences 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 65 Status 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 76 Improved exception handling 77 --------------------------- 78 HTTP 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 94 Lazy handlers 95 ------------- 96 Lazy 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 103 Keyword arguments from URI 104 -------------------------- 105 ``RequestHandler`` methods can also receive keyword arguments, which are easier 106 to maintain than positional ones. Simply use the ``Route`` class to define 107 URIs (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 119 Positional arguments from URI 120 ----------------------------- 121 Positional arguments are also supported, as URI routing is fully compatible 122 with 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 133 Returned responses 134 ------------------ 135 Several Python frameworks adopt the pattern on returning a response object, 136 instead of writing to an existing response object like webapp. For those that 137 prefer, webapp2 supports this: simply return a response object from a handler 138 and 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 149 Custom handler methods 150 ---------------------- 151 webapp2 routing and dispatching system can do a lot more than webapp. 152 For 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 167 View functions 168 -------------- 169 In webapp2 handlers don't need necessarily to be classes. For those that 170 prefer, 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 180 More flexible dispatching mechanism 181 ----------------------------------- 182 The ``WSGIApplication`` in webapp is hard to modify. It dispatches the 183 handler giving little chance to define how it is done, or to pre-process 184 requests before a handler method is actually called. In webapp2 the handlers 185 dispatch themselves, making it easy to implement before and after dispatch 186 hooks. 187 188 webapp2 is thought to be lightweight but flexible. It basically provides an 189 easy to customize URI routing and dispatching mechanisms: you can even extend 190 how URIs are matched or built or how handlers are adapted or dispatched 191 without subclassing. 192 193 For an example of webapp2's flexibility, 194 see :ref:`guide.handlers.a.micro.framework.based.on.webapp2`. 195 196 197 Domain and subdomain routing 198 ---------------------------- 199 webapp2 supports :ref:`domain and subdomain routing <guide.routing.domain-and-subdomain-routing>` 200 to 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 207 Match HTTP methods or URI schemes 208 --------------------------------- 209 webapp2 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 212 that will only match requests using 'https', for example. 213 214 215 URI builder 216 ----------- 217 URIs defined in the aplication can be built. This is more maintanable than 218 hardcoding them in the code or templates. Simply use the ``uri_for()`` 219 function:: 220 221 uri = uri_for('blog-archive', year='2010', month='07') 222 223 And a handler helper for redirects builds the URI to redirect to. 224 redirect_to = redirect + uri_for:: 225 226 self.redirect_to('blog-archive', year='2010', month='07') 227 228 229 Redirection for legacy URIs 230 --------------------------- 231 Old 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 243 Simple, well-tested and documented 244 ---------------------------------- 245 webapp2 is `simple <http://code.google.com/p/webapp-improved/source/browse/webapp2.py>`_, 246 extensively documented and has almost 100% test coverage. The source code is 247 explicit, magic-free and made to be extended. We like less. 248 249 250 Independent of the App Engine SDK 251 --------------------------------- 252 webapp2 doesn't depend on the Google App Engine SDK and 253 :ref:`can be used outside of App Engine <tutorials.quickstart.nogae>`. 254 If the SDK is not found, it has fallbacks to be used in any server as a 255 general purpose web framework. 256 257 258 Future proof 259 ------------ 260 Because it works on threaded environments, webapp2 is ready for when 261 App Engine introduces threading support in the Python 2.7 runtime. 262 263 264 Same performance 265 ---------------- 266 Best of all is that with all these features, there is no loss of performance: 267 cold start times are the same as webapp. Here are some logs of a 'Hello World' 268 cold 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 279 Extras 280 ------ 281 The `webapp2_extras <http://code.google.com/p/webapp-improved/source/browse/#hg%2Fwebapp2_extras>`_ 282 package 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