• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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