Request handling

Created:
April 24, 2023
Updated:
October 31, 2023

FireTail validates incoming requests for conformance with the schemas described in the swagger specification.

Request parameters will be provided to the handler functions as keyword arguments if they are included in the function’s signature, otherwise, body parameters can be accessed from firetail.request.json and query parameters can be accessed from firetail.request.args.

Request validation

Both the request body and parameters are validated against the specification, using jsonschema.

If the request doesn’t match the specification FireTail will return a 400 error.

Automatic parameter handling

FireTail automatically maps the parameters defined in your endpoint specification to arguments of your Python views as named parameters and with value casting whenever possible. All you need to do is define the endpoint’s parameters with matching names with your views arguments.

As an example, you have an endpoint specified as:


paths:
  /foo:
    get:
      operationId: api.foo_get
      parameters:
        - name: message
          description: Some message.
          in: query
          type: string
          required: true

And the view function:


# api.py file

def foo_get(message):
    # do something
    return 'You send the message: {}'.format(message), 200

In this example, FireTail will automatically identify that your view function expects an argument-named message and will assign the value of the endpoint parameter message to your view function.

FireTail will also use default values if they are provided.

If you want to use a parameter name that collides with a Python built-in, you can enable the pythonic_params option:


app = firetail.FlaskApp(__name__)
app.add_api('api.yaml', ..., pythonic_params=True)

With this option enabled, FireTail first converts CamelCase names to snake_case. Then, it looks to see if the name matches a known built-in and if it does it appends an underscore to the name.

As an example, you have an endpoint specified as:


paths:
  /foo:
    get:
      operationId: api.foo_get
      parameters:
        - name: filter
          description: Some filter.
          in: query
          type: string
          required: true

And the view function:


# api.py file
def foo_get(filter_):
    # do something
    return 'You send the filter: {}'.format(filter_), 200
In the OpenAPI 3.x.x spec, the requestBody does not have a name. By default, it will be passed in as ‘body’. You can optionally provide the x-body-name parameter in your operation (or legacy position within the requestBody schema) to override the name of the parameter that will be passed to your handler function.

/path
  post:
    requestBody:
      x-body-name: body
      content:
        application/json:
          schema:
            # legacy location here should be ignored because the preferred location for x-body-name is at the requestBody level above
            x-body-name: this_should_be_ignored

Warning

When you have a parameter defined as not required at your endpoint and your Python view has a non-named argument, when you call this endpoint without the parameter you will get an exception of missing positional argument.

Type casting

Whenever possible FireTail will try to parse your argument values and do type casting to related Python native values. The current available type castings are:

OpenAPI TypePython Type
integerint
stringstr
numberfloat
booleanbool
arraylist
nullNone
objectdict
For more details about collectionFormats, visit the official OpenAPI 2.0 Specification.

In the OpenAPI 2.0 Specification if you use the array type, you can define the collectionFormat to set the deserialization behavior. FireTail currently supports “pipes” and “csv” as collection formats. The default format is “csv”.

FireTail is opinionated about how the URI is parsed for array types. The default behavior for query parameters that have been defined multiple times is to join them all together. For example, if you provide a URI with the query string ?letters=a,b,c&letters=d,e,f, FireTail will set letters = ['a', 'b', 'c', 'd', 'e', 'f'].

You can override this behavior by specifying the URI parser in the app or API options.


from firetail.decorators.uri_parsing import Swagger2URIParser
options = {'uri_parser_class': Swagger2URIParser}
app = firetail.App(__name__, specification_dir='swagger/', options=options)

You can implement your own URI parsing behavior by inheriting from firetail.decorators.uri_parsing.AbstractURIParser.

There are a handful of URI parsers included with the connection.

OpenAPIURIParser default: OpenAPI 3.0This parser adheres to the OpenAPI 3.x.x spec, and uses the style parameter. Query parameters are parsed from left to right, so if a query parameter is defined twice, then the right-most definition will take precedence. For example, if you provided a URI with the query string ?letters=a,b,c&letters=d,e,f, and style: simple, then firetail will set letters = ['d', 'e', 'f']. For additional information see OpenAPI 3.0 Style Values.
Swagger2URIParser default: OpenAPI 2.0This parser adheres to the Swagger 2.0 spec, and will only join together multiple instance of the same query parameter if the collectionFormat is set to multi. Query parameters are parsed from left to right, so if a query parameter is defined twice, then the right-most definition wins. For example, if you provided a URI with the query string ?letters=a,b,c&letters=d,e,f, and collectionFormat: csv, then firetail will set letters = ['d', 'e', 'f']
FirstValueURIParserThis parser behaves like the Swagger2URIParser, except that it prefers the first defined value. For example, if you provided a URI with the query string ?letters=a,b,c&letters=d,e,f and collectionFormat: csv hen firetail will set letters = ['a', 'b', 'c']
AlwaysMultiURIParserThis parser is backwards compatible with Firetail 1.x. It joins together multiple instances of the same query parameter.

Parameter validation

FireTail can apply strict parameter validation for query and form data parameters. When this is enabled, requests that include parameters not defined in the swagger spec return a 400 error. You can enable it when adding the API to your application:


app.add_api('my_apy.yaml', strict_validation=True)

Nullable parameters

Sometimes your API should explicitly accept nullable parameters. However OpenAPI specification currently does not support officially a way to serve this use case, FireTail adds the x-nullable vendor extension to parameter definitions. Its usage would be:


/countries/cities:
   parameters:
     - name: name
       in: query
       type: string
       x-nullable: true
       required: true

It is supported by FireTail in all parameter types: body, query, formData, and path. Nullable values are the strings null and None.

Warning

Be careful on nullable parameters for sensitive data where the strings “null” or “None” can be valid values.

Note

This extension will be removed as soon as OpenAPI/Swagger Specification provides an official way of supporting nullable values.

Header parameters

Currently, header parameters are not passed to the handler functions as parameters. However, they can be accessed through the underlying firetail.request.headers object which aliases the flask.request.headers object.


def index():
    page_number = firetail.request.headers['Page-Number']

Custom validators

By default, body and parameters contents are validated against OpenAPI schema via firetail.decorators.validation.RequestBodyValidator or firetail.decorators.validation.ParameterValidator, if you want to change the validation, you can override the defaults with:


validator_map = {
    'body': CustomRequestBodyValidator,
    'parameter': CustomParameterValidator
}
app = firetail.FlaskApp(__name__)
app.add_api('api.yaml', ..., validator_map=validator_map)

See a custom validator example in examples/enforcedefaults.

Related topics