Validation

One of the most powerful Connexion features is automatic validation based on your OpenAPI specification.

Connexion validates:

The validation behavior can easily be customized with Custom validators

Request validation

Connexion will validate any incoming requests against your specification and automatically returns the correct 4XX error on failure.

Parameter validation

By default, Connexion checks all the request for any parameters defined in your specification and validates them against their definition. This includes their schema (type, format, range, …) and whether or not they are required or whether they can be null.

You can turn on strict_validation if you want Connexion to disallow any extra parameters that are not defined in your specification. You can set it either on the application or API level:

app.py
from connexion import AsyncApp

app = AsyncApp(__name__, strict_validation=True)
app.add_api("openapi.yaml", strict_validation=True)
app.py
from connexion import FlaskApp

app = FlaskApp(__name__, strict_validation=True)
app.add_api("openapi.yaml", strict_validation=True)
app.py
from asgi_framework import App
from connexion import ConnexionMiddleware

app = App(__name__)
app = ConnexionMiddleware(app, strict_validation=True)
app.add_api("openapi.yaml", strict_validation=True)

If parameter validation fails, Connexion will return a 400 Bad Request response with information on the failure in the description.

For more information on how parameters are handled in general, see Request handling.

RequestBody validation

Connexion can automatically validate a requestBody for json and formData content types, for which it relies on jsonschema. You can plug in your own validator for other content types (see Custom validators).

Note

If the Content-Type header is not set in the request, Connexion will check your specification for which content types it accepts. If it only accepts a single content type, Connexion assumes the request to have this content type and will validate it accordingly. If your specification specifies no or multiple content types it accepts, Connexion will assume the request to have content type application/octet-stream; charset=utf-8 and will skip requestBody validation.

If requestBody validation fails, Connexion will return a 400 Bad Request response with information on the failure in the description.

For more information on how the requestBody is handled in general, see Body.

Request headers validation

Headers and cookies are also validated against your specification. If their validation fails, Connexion will return a 400 Bad Request response with information on the failure in the description.

The Content-Type header is validated separately. If it fails validation, Connexion returns a 415 Unsupported Media Type error.

Note

If the Content-Type header is not set in the request, Connexion will make an assumption on the content type (see RequestBody validation) and validate it against your spec, which might fail.

Response validation

Connexion will not validate outgoing responses by default , but you can activate this by passing the validate_responses argument to either your application or API:

app.py
from connexion import AsyncApp

app = AsyncApp(__name__, validate_responses=True)
app.add_api("openapi.yaml", validate_responses=True)
app.py
from connexion import FlaskApp

app = FlaskApp(__name__, validate_responses=True)
app.add_api("openapi.yaml", validate_responses=True)
app.py
from asgi_framework import App
from connexion import ConnexionMiddleware

app = App(__name__)
app = ConnexionMiddleware(app, validate_responses=True)
app.add_api("openapi.yaml", validate_responses=True)

ResponseBody validation

Connexion has built-in validators for the application/json and text/plain content types. If the content type is not explicitly set, Connexion will infer it (see Headers), and validate the body using the corresponding validator.

Response headers validation

Connexion will check for any required response headers that are missing and will validate the Content-Type header against the responses defined in your specification.

Note

If the content type is not explicitly set, Connexion will infer it (see Headers), and validate the inferred content type, which can still fail.

Custom validators

Connexion provides a validator_map argument which you can use to pass in custom validators. The default validators are defined in connexion.validators.VALIDATOR_MAP:

connexion.validators
VALIDATOR_MAP = {
    "parameter": ParameterValidator,
    "body": MediaTypeDict(
        {
            "*/*json": JSONRequestBodyValidator,
            "application/x-www-form-urlencoded": FormDataValidator,
            "multipart/form-data": MultiPartFormDataValidator,
        }
    ),
    "response": MediaTypeDict(
        {
            "*/*json": JSONResponseBodyValidator,
            "text/plain": TextResponseBodyValidator,
        }
    ),
}

Note that the "body" and "response" values are instances of the special MediaTypeDict datastructure, which can handle Media Type ranges:

class connexion.datastructures.MediaTypeDict

A dictionary where keys can be either media types or media type ranges. When fetching a value from the dictionary, the provided key is checked against the ranges. The most specific key is chosen as prescribed by the OpenAPI spec, with type/* being preferred above */subtype.

You can create your own custom Validator by subclassing the connexion.validators.AbstractRequestBodyValidator or connexion.validators.AbstractResponseBodyValidator class and override the defaults by passing in a custom validator_map to your application or API:

app.py
from connexion.datastructures import MediaTypeDict
from connexion.validators import AbstractResponseBodyValidator, TextResponseBodyValidator


class MyCustomXMLResponseValidator(AbstractResponseBodyValidator):

    def _parse(self, stream: t.Generator[bytes, None, None]) -> t.Any:
        ...

    def _validate(self, body: dict):
        ...


validator_map = {
    "response": MediaTypeDict(
        {
            "*/*json": JSONResponseBodyValidator,
            "*/*xml": MyCustomXMLResponseValidator,
            "text/plain": TextResponseBodyValidator,
        }
    ),
}
app.py
from connexion import AsyncApp

app = AsyncApp(__name__, validator_map=validator_map)
app.add_api("openapi.yaml", validator_map=validator_map)
app.py
from connexion import FlaskApp

app = FlaskApp(__name__, validator_map=validator_map)
app.add_api("openapi.yaml", validator_map=validator_map)
app.py
from asgi_framework import App
from connexion import ConnexionMiddleware

app = App(__name__)
app = ConnexionMiddleware(app, validator_map=validator_map)
app.add_api("openapi.yaml", validator_map=validator_map)

Note that this will override the "response" section of the default VALIDATOR_MAP, and the "response" section only. This means that you need to include all ResponseValidators that you want to be active, or they will be removed.

If you want to deactivate request validation, you can pass in an empty dictionary:

validator_map = {
    "body": {}
}

Which you then pass into your application or API as mentioned above.

Inserting requestBody defaults

You can let Connexion automatically insert default values as defined in your specification into an incoming requestBody by configuring the DefaultsJSONRequestBodyValidator:

app.py
from connexion.datastructures import MediaTypeDict
from connexion.validators import (
    DefaultsJSONRequestBodyValidator,
    FormDataValidator,
    MultiPartFormDataValidator,
)

validator_map = {
    "body": MediaTypeDict(
        {
            "*/*json": DefaultsJSONRequestBodyValidator,
            "application/x-www-form-urlencoded": FormDataValidator,
            "multipart/form-data": MultiPartFormDataValidator,
        }
    ),
}

Which you then pass into your application or API as mentioned above.

See our enforce defaults example for a full example.

Custom type formats

It is possible to define custom type formats for validation without adding a custom validator, by leveraging the jsonschema.draft4_format_checker.checks decorator.

We can for instance create a custom money format.

import re
from jsonschema import draft4_format_checker

MONEY_RE = re.compile('^\$\s*\d+(\.\d\d)?')

@draft4_format_checker.checks('money')
def is_money(val):
    if not isinstance(val, str):
        return True
    return MONEY_RE.match(val)

Which you can then use in your openAPI specification:

type: object
properties:
  title:
    type: string
  price_label:
    type: string
    format: money

The format checker function is expected to return True when the value matches the expected format and return False when it doesn’t. Also is important to verify if the type of the value you are trying to validate is compatible with the format. In our example we check if the val is of type “string” before performing any further checking.

Note

Keep in mind that the format checkers should be defined and registered before you run your application server.