| .. _features: |
| |
| webapp2 features |
| ================ |
| Here's an overview of the main improvements of webapp2 compared to webapp. |
| |
| .. contents:: Table of Contents |
| :depth: 3 |
| :backlinks: none |
| |
| |
| Compatible with webapp |
| ---------------------- |
| webapp2 is designed to work with existing webapp apps without any changes. |
| See how this looks familiar:: |
| |
| import webapp2 as webapp |
| from google.appengine.ext.webapp.util import run_wsgi_app |
| |
| class HelloWorldHandler(webapp.RequestHandler): |
| def get(self): |
| self.response.out.write('Hello, World!') |
| |
| app = webapp.WSGIApplication([ |
| ('/', HelloWorldHandler), |
| ], debug=True) |
| |
| def main(): |
| run_wsgi_app(app) |
| |
| if __name__ == '__main__': |
| main() |
| |
| Everybody starting with App Engine must know a bit of webapp. And you you can |
| use webapp2 exactly like webapp, following the official tutorials, and learn |
| the new features later, as you go. This makes webapp2 insanely easy to learn. |
| |
| Also, the SDK libraries that use webapp can be used with webapp2 as they are |
| or with minimal adaptations. |
| |
| |
| Compatible with latest WebOb |
| ---------------------------- |
| The ``WebOb`` version included in the App Engine SDK was released in 2008. |
| Since then many bugs were fixed and the source code became cleaner and better |
| documented. webapp2 is compatible with the ``WebOb`` version included in the |
| SDK, but for those that prefer the latest version can be used as well. |
| This avoids the bugs |
| `#170 <http://code.google.com/p/googleappengine/issues/detail?id=170>`_, |
| `#719 <http://code.google.com/p/googleappengine/issues/detail?id=719>`_ and |
| `#2788 <http://code.google.com/p/googleappengine/issues/detail?id=2788>`_, |
| at least. |
| |
| |
| Full-featured response object |
| ----------------------------- |
| webapp2 uses a full-featured response object from ``WebOb``. If offers several |
| conveniences to set headers, like easy cookies and other goodies:: |
| |
| class MyHandler(webapp2.RequestHandler): |
| def get(self): |
| self.response.set_cookie('key', 'value', max_age=360, path='/') |
| |
| |
| Status code exceptions |
| ---------------------- |
| ``abort()`` (or ``self.abort()`` inside handlers) raises a proper |
| ``HTTPException`` (from ``WebOb``) and stops processing:: |
| |
| # Raise a 'Not Found' exception and let the 404 error handler do its job. |
| abort(404) |
| # Raise a 'Forbidden' exception and let the 403 error handler do its job. |
| self.abort(403) |
| |
| |
| Improved exception handling |
| --------------------------- |
| HTTP exceptions can also be handled by the WSGI application:: |
| |
| # ... |
| import logging |
| |
| def handle_404(request, response, exception): |
| logging.exception(exception) |
| response.write('Oops! I could swear this page was here!') |
| response.set_status(404) |
| |
| app = webapp2.WSGIApplication([ |
| ('/', MyHandler), |
| ]) |
| app.error_handlers[404] = handle_404 |
| |
| |
| Lazy handlers |
| ------------- |
| Lazy handlers can be defined as a string to be imported only when needed:: |
| |
| app = webapp2.WSGIApplication([ |
| ('/', 'my.module.MyHandler'), |
| ]) |
| |
| |
| Keyword arguments from URI |
| -------------------------- |
| ``RequestHandler`` methods can also receive keyword arguments, which are easier |
| to maintain than positional ones. Simply use the ``Route`` class to define |
| URIs (and you can also create custom route classes, examples |
| `here <http://code.google.com/p/webapp-improved/source/browse/webapp2_extras/routes.py>`_):: |
| |
| class BlogArchiveHandler(webapp2.RequestHandler): |
| def get(self, year=None, month=None): |
| self.response.write('Hello, keyword arguments world!') |
| |
| app = webapp2.WSGIApplication([ |
| webapp2.Route('/<year:\d{4}>/<month:\d{2}>', handler=BlogArchiveHandler, name='blog-archive'), |
| ]) |
| |
| |
| Positional arguments from URI |
| ----------------------------- |
| Positional arguments are also supported, as URI routing is fully compatible |
| with webapp:: |
| |
| class BlogArchiveHandler(webapp2.RequestHandler): |
| def get(self, year, month): |
| self.response.write('Hello, webapp routing world!') |
| |
| app = webapp2.WSGIApplication([ |
| ('/(\d{4})/(\d{2})', BlogArchiveHandler), |
| ]) |
| |
| |
| Returned responses |
| ------------------ |
| Several Python frameworks adopt the pattern on returning a response object, |
| instead of writing to an existing response object like webapp. For those that |
| prefer, webapp2 supports this: simply return a response object from a handler |
| and it will be used instead of the one created by the application:: |
| |
| class BlogArchiveHandler(webapp2.RequestHandler): |
| def get(self): |
| return webapp2.Response('Hello, returned response world!') |
| |
| app = webapp2.WSGIApplication([ |
| webapp2.Route('/', handler=HomeHandler, name='home'), |
| ]) |
| |
| |
| Custom handler methods |
| ---------------------- |
| webapp2 routing and dispatching system can do a lot more than webapp. |
| For example, handlers can also use custom methods:: |
| |
| class MyHandler(webapp2.RequestHandler): |
| def my_custom_method(self): |
| self.response.write('Hello, custom method world!') |
| |
| def my_other_method(self): |
| self.response.write('Hello, another custom method world!') |
| |
| app = webapp2.WSGIApplication([ |
| webapp2.Route('/', handler=MyHandler, name='custom-1', handler_method='my_custom_method'), |
| webapp2.Route('/other', handler=MyHandler, name='custom-2', handler_method='my_other_method'), |
| ]) |
| |
| |
| View functions |
| -------------- |
| In webapp2 handlers don't need necessarily to be classes. For those that |
| prefer, functions can be used as well:: |
| |
| def my_sweet_function(request, *args, **kwargs): |
| return webapp2.Response('Hello, function world!') |
| |
| app = webapp2.WSGIApplication([ |
| webapp2.Route('/', handler=my_sweet_function, name='home'), |
| ]) |
| |
| |
| More flexible dispatching mechanism |
| ----------------------------------- |
| The ``WSGIApplication`` in webapp is hard to modify. It dispatches the |
| handler giving little chance to define how it is done, or to pre-process |
| requests before a handler method is actually called. In webapp2 the handlers |
| dispatch themselves, making it easy to implement before and after dispatch |
| hooks. |
| |
| webapp2 is thought to be lightweight but flexible. It basically provides an |
| easy to customize URI routing and dispatching mechanisms: you can even extend |
| how URIs are matched or built or how handlers are adapted or dispatched |
| without subclassing. |
| |
| For an example of webapp2's flexibility, |
| see :ref:`guide.handlers.a.micro.framework.based.on.webapp2`. |
| |
| |
| Domain and subdomain routing |
| ---------------------------- |
| webapp2 supports :ref:`domain and subdomain routing <guide.routing.domain-and-subdomain-routing>` |
| to restrict URI matches based on the server name:: |
| |
| routes.DomainRoute('www.mydomain.com', [ |
| webapp2.Route('/', handler=HomeHandler, name='home'), |
| ]) |
| |
| |
| Match HTTP methods or URI schemes |
| --------------------------------- |
| webapp2 routing system allows routes to be restricted to the |
| :ref:`HTTP method <guide.routing.restricting-http-methods>` or a specific |
| :ref:`URI scheme <guide.routing.restricting-uri-schemes>`. You can set routes |
| that will only match requests using 'https', for example. |
| |
| |
| URI builder |
| ----------- |
| URIs defined in the aplication can be built. This is more maintanable than |
| hardcoding them in the code or templates. Simply use the ``uri_for()`` |
| function:: |
| |
| uri = uri_for('blog-archive', year='2010', month='07') |
| |
| And a handler helper for redirects builds the URI to redirect to. |
| redirect_to = redirect + uri_for:: |
| |
| self.redirect_to('blog-archive', year='2010', month='07') |
| |
| |
| Redirection for legacy URIs |
| --------------------------- |
| Old URIs can be conveniently redirected using a simple route:: |
| |
| def get_redirect_uri(handler, *args, **kwargs): |
| return handler.uri_for('view', item=kwargs.get('item')) |
| |
| app = webapp2.WSGIApplication([ |
| webapp2.Route('/view/<item>', ViewHandler, 'view'), |
| webapp2.Route('/old-page', RedirectHandler, defaults={'uri': '/view/i-came-from-a-redirect'}), |
| webapp2.Route('/old-view/<item>', RedirectHandler, defaults={'uri': get_redirect_uri}), |
| ]) |
| |
| |
| Simple, well-tested and documented |
| ---------------------------------- |
| webapp2 is `simple <http://code.google.com/p/webapp-improved/source/browse/webapp2.py>`_, |
| extensively documented and has almost 100% test coverage. The source code is |
| explicit, magic-free and made to be extended. We like less. |
| |
| |
| Independent of the App Engine SDK |
| --------------------------------- |
| webapp2 doesn't depend on the Google App Engine SDK and |
| :ref:`can be used outside of App Engine <tutorials.quickstart.nogae>`. |
| If the SDK is not found, it has fallbacks to be used in any server as a |
| general purpose web framework. |
| |
| |
| Future proof |
| ------------ |
| Because it works on threaded environments, webapp2 is ready for when |
| App Engine introduces threading support in the Python 2.7 runtime. |
| |
| |
| Same performance |
| ---------------- |
| Best of all is that with all these features, there is no loss of performance: |
| cold start times are the same as webapp. Here are some logs of a 'Hello World' |
| cold start: |
| |
| .. code-block:: text |
| |
| 100ms 77cpu_ms |
| 143ms 58cpu_ms |
| 155ms 77cpu_ms |
| 197ms 96cpu_ms |
| 106ms 77cpu_ms |
| |
| |
| Extras |
| ------ |
| The `webapp2_extras <http://code.google.com/p/webapp-improved/source/browse/#hg%2Fwebapp2_extras>`_ |
| package provides common utilities that integrate well with webapp2: |
| |
| - Localization and internationalization support |
| - Sessions using secure cookies, memcache or datastore |
| - Extra route classes -- to match subdomains and other conveniences |
| - Support for third party libraries: Jinja2 and Mako |
| - Support for threaded environments, so that you can use webapp2 outside of |
| App Engine or in the upcoming App Engine Python 2.7 runtime |