Home | History | Annotate | Download | only in docs
      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