# 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](https://docs.python.org/3/library/dataclasses.html), 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,
    )
    ```

