3.4 KiB
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, 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 of the box. If you also have either pydantic
or attrs
installed, you can alternatively use one of those libraries.
.. column::
First, define a model.
.. column::
```python
@dataclass
class SearchParams:
q: str
```
.. column::
Then, attach it to your route
.. column::
```python
from sanic_ext import validate
@app.route("/search")
@validate(query=SearchParams)
async def handler(request, query: SearchParams):
return json(asdict(query))
```
.. column::
You should now have validation on the incoming request.
.. column::
```
$ 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.
.. column::
First, define a model.
.. column::
```python
class Person(BaseModel):
name: str
age: int
```
.. column::
Then, attach it to your route
.. column::
```python
from sanic_ext import validate
@app.post("/person")
@validate(json=Person)
async def handler(request, body: Person):
return json(body.dict())
```
.. column::
You should now have validation on the incoming request.
.. column::
```
$ curl localhost:8000/person -d '{"name": "Alice", "age": 21}' -X POST
{"name":"Alice","age":21}
```
Validation with Attrs
You can use Attrs also.
.. column::
First, define a model.
.. column::
```python
@attrs.define
class Person:
name: str
age: int
```
.. column::
Then, attach it to your route
.. column::
```python
from sanic_ext import validate
@app.post("/person")
@validate(json=Person)
async def handler(request, body: Person):
return json(attrs.asdict(body))
```
.. column::
You should now have validation on the incoming request.
.. column::
```
$ 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 data from three places: JSON body data (request.json
), form body data (request.form
), and query parameters (request.args
).
.. column::
As you might expect, you can attach your model using the keyword arguments of the decorator.
.. column::
```python
@validate(
json=ModelA,
query=ModelB,
form=ModelC,
)
```