webargs

Framework Support

This section includes notes for using webargs with specific web frameworks.

Flask

Flask support is available via the webargs.flaskparser module.

Decorator Usage

When using the use_args decorator, the arguments dictionary will be before any URL variable parameters.

from webargs import fields
from webargs.flaskparser import use_args

@app.route('/user/<int:uid>')
@use_args({'per_page': fields.Int()})
def user_detail(args, uid):
    return ('The user page for user {uid}, '
            'showing {per_page} posts.').format(uid=uid,
                                                per_page=args['per_page'])

Error Handling

Webargs uses Flask’s abort function to raise an HTTPException when a validation error occurs. If you use the Flask.errorhandler method to handle errors, you can access validation messages from the data attribute of an error.

Here is an example error handler that returns validation messages to the client as JSON.

from flask import jsonify

@app.errorhandler(422)
def handle_unprocessable_entity(err):
    # webargs attaches additional metadata to the `data` attribute
    exc = getattr(err, 'exc')
    if exc:
        # Get validations from the ValidationError object
        messages = exc.messages
    else:
        messages = ['Invalid request']
    return jsonify({
        'messages': messages,
    }), 422

URL Matches

The FlaskParser supports parsing values from a request’s view_args.

from webargs.flaskparser import use_args

@app.route('/greeting/<name>/')
@use_args({'name': fields.Str(location='view_args')})
def greeting(args, **kwargs):
    return 'Hello {}'.format(args['name'])

Django

Django support is available via the webargs.djangoparser module.

Webargs can parse Django request arguments in both function-based and class-based views.

Decorator Usage

When using the use_args decorator, the arguments dictionary will positioned after the request argument.

Function-based Views

from django.http import HttpResponse
from webargs import Arg
from webargs.djangoparser import use_args

account_args = {
  'username': fields.Str(required=True),
  'password': fields.Str(required=True),
}

@use_args(account_args)
def login_user(request, args):
    if request.method == 'POST':
        login(args['username'], args['password'])
    return HttpResponse('Login page')

Class-based Views

from django.views.generic import View
from django.shortcuts import render_to_response
from webargs import fields
from webargs.djangoparser import use_args

blog_args = {
    'title': fields.Str(),
    'author': fields.Str(),
}

class BlogPostView(View):

    @use_args(blog_args)
    def get(self, request, args):
      blog_post = Post.objects.get(title__iexact=args['title'],
                                   author=args['author'])
      return render_to_response('post_template.html',
                                {'post': blog_post})

Error Handling

The DjangoParser does not override handle_error, so your Django views are responsible for catching any ValidationErrors raised by the parser and returning the appropriate HTTPResponse.

from django.http import JsonResponse

from webargs import fields, ValidationError

argmap = {
    'name': fields.Str(required=True)
}
def index(request):
    try:
        args = parser.parse(argmap, request)
    except ValidationError as err:
        return JsonResponse(err.messages, status=err.status_code)
    return JsonResponse({'message': 'Hello {name}'.format(name=name)})

Tornado

Tornado argument parsing is available via the webargs.tornadoparser module.

The webargs.tornadoparser.TornadoParser parses arguments from a tornado.httpserver.HTTPRequest object. The TornadoParser can be used directly, or you can decorate handler methods with use_args or use_kwargs.

import tornado.ioloop
import tornado.web

from webargs import fields
from webargs.tornadoparser import parser


class HelloHandler(tornado.web.RequestHandler):

    hello_args = {
        'name': fields.Str()
    }

    def post(self, id):
        reqargs = parser.parse(self.hello_args, self.request)
        response = {
            'message': 'Hello {}'.format(reqargs['name'])
        }
        self.write(response)

application = tornado.web.Application([
    (r"/hello/([0-9]+)", HelloHandler),
], debug=True)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Decorator Usage

When using the use_args decorator, the decorated method will have the dictionary of parsed arguments passed as a positional argument after self and any regex match groups from the URL spec.

from webargs import fields
from webargs.tornadoparser import use_args

class HelloHandler(tornado.web.RequestHandler):

    @use_args({'name': fields.Str()})
    def post(self, id, reqargs):
        response = {
            'message': 'Hello {}'.format(reqargs['name'])
        }
        self.write(response)

application = tornado.web.Application([
    (r"/hello/([0-9]+)", HelloHandler),
], debug=True)

As with the other parser modules, use_kwargs will add keyword arguments to the view callable.

Error Handling

A HTTPError will be raised in the event of a validation error. Your RequestHandlers are responsible for handling these errors.

Here is how you could write the error messages to a JSON response.

from tornado.web import RequestHandler

class MyRequestHandler(RequestHandler):

    def write_error(self, status_code, **kwargs):
        """Write errors as JSON."""
        self.set_header('Content-Type', 'application/json')
        if 'exc_info' in kwargs:
            etype, value, traceback = kwargs['exc_info']
            if hasattr(value, 'messages'):
                self.write({'errors': value.messages})
                self.finish()

Pyramid

Pyramid support is available via the webargs.pyramidparser module.

Decorator Usage

When using the use_args decorator on a view callable, the arguments dictionary will be positioned after the request argument.

from pyramid.response import Response
from webargs import fields
from webargs.pyramidparser import use_args

@use_args({'per_page': fields.Int()})
def user_detail(request, args):
    return Response('The user page for user {uid}, '
            'showing {per_page} posts.').format(uid=uid,
                                                per_page=args['per_page']))

As with the other parser modules, use_kwargs will add keyword arguments to the view callable.

URL Matches

The PyramidParser supports parsing values from a request’s matchdict.

from pyramid.response import Response
from webargs.pyramidparser import use_args

@parser.use_args({'mymatch': fields.Int()}, locations=('matchdict',))
def matched(request, args):
    return Response('The value for mymatch is {}'.format(args['mymatch'])))

Falcon

Falcon support is available via the webargs.falconparser module.

Decorator Usage

When using the use_args decorator on a resource method, the arguments dictionary will be positioned directly after the request and response arguments.

import falcon
from webargs import fields
from webargs.falconparser import use_args

class BlogResource:
    request_args = {
        'title': fields.Str(required=True)
    }

    @use_args(request_args)
    def on_post(self, req, resp, args, post_id):
        content = args['title']
        # ...

api = application = falcon.API()
api.add_route('/blogs/{post_id}')

As with the other parser modules, use_kwargs will add keyword arguments to your resource methods.

Hook Usage

You can easily implement hooks by using parser.parse directly.

import falcon
from webargs import fields
from webargs.falconparser import parser

def add_args(argmap, **kwargs):
    def hook(req, resp, params):
        parsed_args = parser.parse(argmap, req=req, **kwargs)
        req.context['args'] = parsed_args
    return hook

@falcon.before(add_args({'page': fields.Int(location='query')}))
class AuthorResource:

    def on_get(self, req, resp):
        args = req.context['args']
        page = args.get('page')
        # ...

aiohttp

aiohttp support is available via the webargs.aiohttpparser module.

The parse method of AIOHTTPParser is a coroutine.

import asyncio

from aiohttp import web
from webargs import fields
from webargs.aiohttpparser import parser

handler_args = {
    'name': fields.Str(missing='World')
}
@asyncio.coroutine
def handler(request):
    args = yield from parser.parse(handler_args, request)
    return web.Response(
        body='Hello, {}'.format(args['name']).encode('utf-8')
    )

Decorator Usage

When using the use_args decorator on a handler, the parsed arguments dictionary will be the last positional argument.

import asyncio

from aiohttp import web
from webargs import fields
from webargs.aiohttpparser import use_args

@asyncio.coroutine
@use_args({'content': fields.Str(required=True)})
def create_comment(request, args):
    content = args['content']
    # ...

app = web.Application()
app.router.add_route('POST', '/comments/', create_comment)

As with the other parser modules, use_kwargs will add keyword arguments to your resource methods.

Usage with coroutines

The use_args and use_kwargs decorators will not work with async def coroutines. You must either use a generator-based coroutine decorated with asyncio.coroutine or use parser.parse.

from aiohttp import web

from webargs import fields

hello_args = {
    'name': fields.Str(missing='World')
}

# YES
from webargs.aiohttpparser import parser

async def hello(request):
    args = await parser.parse(hello_args, request)
    return web.Response(
        body='Hello, {}'.format(name).encode('utf-8')
    )

# YES
import asyncio
from webargs.aiohttpparser import use_kwargs

@asyncio.coroutine
@use_kwargs(hello_args)
def hello(request, name):
    return web.Response(
        body='Hello, {}'.format(name).encode('utf-8')
    )

# NO: use_args and use_kwargs are incompatible with async def
@use_kwargs(hello_args)
async def hello(request, name):
    return web.Response(
        body='Hello, {}'.format(name).encode('utf-8')
    )

URL Matches

The AIOHTTPParser supports parsing values from a request’s match_info.

from aiohttp import web
from webargs.aiohttpparser import use_args

@parser.use_args({'slug': fields.Str(location='match_info')})
def article_detail(request, args):
    return web.Response(
        body='Slug: {}'.format(args['slug']).encode('utf-8')
    )

app = web.Application()
app.router.add_route('GET', '/articles/{slug}', article_detail)