# Validation

One of the most commonly implemented features of a web application is user-input validation. For obvious reasons, this is not only a security issue, but also just plain good practice. You want to make sure your data conforms to expectations, and throw a 400 response when it does not.

# Implementation

# Validation with Dataclasses

With the introduction of Data Classes (opens new window), Python made it super simple to create objects that meet a defined schema. However, the standard library only supports type checking validation, not runtime validation. Sanic Extensions adds the ability to do runtime validations on incoming requests using dataclasses out odf the box. If you also have either pydantic or attrs installed, you can alternatively use one of those libraries.

First, define a model.

@dataclass
class SearchParams:
    q: str

Then, attach it to your route

from sanic_ext import validate
@app.route("/search")
@validate(query=SearchParams)
async def handler(request, query: SearchParams):
    return json(asdict(query))

You should now have validation on the incoming request.

$ curl localhost:8000/search                                       
⚠️ 400 — Bad Request
====================
Invalid request body: SearchParams. Error: missing a required argument: 'q'
$ curl localhost:8000/search\?q=python                             
{"q":"python"}

# Validation with Pydantic

You can use Pydantic models also.

First, define a model.

class Person(BaseModel):
    name: str
    age: int

Then, attach it to your route

from sanic_ext import validate
@app.post("/person")
@validate(json=Person)
async def handler(request, body: Person):
    return json(body.dict())

You should now have validation on the incoming request.

$ curl localhost:8000/person -d '{"name": "Alice", "age": 21}' -X POST  
{"name":"Alice","age":21}

# Validation with Attrs

You can use Attrs also.

First, define a model.

@attrs.define
class Person:
    name: str
    age: int

Then, attach it to your route

from sanic_ext import validate
@app.post("/person")
@validate(json=Person)
async def handler(request, body: Person):
    return json(attrs.asdict(body))

You should now have validation on the incoming request.

$ curl localhost:8000/person -d '{"name": "Alice", "age": 21}' -X POST  
{"name":"Alice","age":21}

# What can be validated?

The validate decorator can be used to validate incoming user date from three places: JSON body data (request.json), form body data (request.form), and query parameters (request.args).

As you might expect, you can attach your model using the keyword arguments of the decorator.

@validate(
    json=ModelA,
    query=ModelB,
    form=ModelC,
)
MIT Licensed
Copyright © 2018-present Sanic Community Organization

~ Made with ❤️ and ☕️ ~