- Notifications
You must be signed in to change notification settings - Fork12
Get and validate all Flask input parameters with ease.
Ge0rg3/flask-parameter-validation
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
- Pip: Install with
pip install flask_parameter_validation. - Manually:
git clone https://github.com/Ge0rg3/flask-parameter-validation.gitpython setup.py install
fromflaskimportFlaskfromtypingimportOptionalfromflask_parameter_validationimportValidateParameters,Route,Json,QueryfromdatetimeimportdatetimefromenumimportEnumclassAccountStatus(int,Enum):# In Python 3.11 or later, subclass IntEnum from enum package instead of int, EnumACTIVE=1DISABLED=0classUserType(str,Enum):# In Python 3.11 or later, subclass StrEnum from enum package instead of str, EnumUSER="user"SERVICE="service"app=Flask(__name__)@app.route("/update/<int:id>",methods=["POST"])@ValidateParameters()defhello(id:int=Route(),username:str=Json(min_str_length=5,blacklist="<>"),age:int=Json(min_int=18,max_int=99),nicknames:list[str]=Json(),date_of_birth:datetime=Json(),password_expiry:Optional[int]=Json(5),is_admin:bool=Query(False),user_type:UserType=Json(alias="type"),status:AccountStatus=Json() ):return"Hello World!"if__name__=="__main__":app.run()
To validate parameters with flask-parameter-validation, two conditions must be met.
- The
@ValidateParameters()decorator must be applied to the function - Type hints (supported types) and a default of a subclass of
Parametermust be supplied per parameter
The@ValidateParameters() decorator takes parameters that alter route validation behavior or provide documentation information:
| Parameter | Type | Default | Description |
|---|---|---|---|
| error_handler | Optional[Response] | None | Overwrite the output format of generated errors, seeOverwriting Default Errors for more |
By default, the error messages are returned as a JSON response, with the detailed error in the "error" field, eg:
{"error":"Parameter 'age' must be type 'int'"}However, this can be edited by passing a custom error function into theValidateParameters() decorator. For example:
deferror_handler(err):error_name=type(err)error_parameters=err.argserror_message=str(err)return {"error_name":type(err).__name__,"error_parameters":err.args,"error_message":str(err) },400@app.route(...)@ValidateParameters(error_handler)defapi(...)
TheParameter class provides a base for validation common among all input types, all location-specific classes extendParameter. These subclasses are:
| Subclass Name | Input Source | Available For |
|---|---|---|
| Route | Parameter passed in the pathname of the URL, such as/users/<int:id> | All HTTP Methods |
| Form | Parameter in an HTML form or aFormData object in the request body, often withContent-Type: x-www-form-urlencoded | POST Methods |
| Json | Parameter in the JSON object in the request body, must have headerContent-Type: application/json | POST Methods |
| Query | Parameter in the query of the URL, such as /news_article?id=55 | All HTTP Methods |
| File | Parameter is a file uploaded in the request body | POST Method |
| MultiSource | Parameter is in one of the locations provided to the constructor | Dependent on selected locations |
Note: "POST Methods" refers to the HTTP methods that send data in the request body, such as POST, PUT, PATCH and DELETE. Although sending data via some methods such as DELETE is not standard, it is supported by Flask and this library.
Using theMultiSource parameter type, parameters can be accepted from any combination ofParameter subclasses. Example usage is as follows:
@app.route("/")@app.route("/<v>")# If accepting parameters by Route and another type, a path with and without that Route parameter must be specified@ValidateParameters()defmulti_source_example(value:int=MultiSource(Route,Query,Json,min_int=0))
The above example will accept parameters passed to the route through Route, Query, and JSON Body.
Note: "POST Methods" refers to the HTTP methods that send data in the request body, such as POST, PUT, PATCH and DELETE. Although sending data via some methods such as DELETE is not standard, it is supported by Flask and this library.
Type Hints allow for inline specification of the input type of a parameter. Some types are only available to certainParameter subclasses.
| Type Hint / Expected Python Type | Notes | Route | Form | Json | Query | File |
|---|---|---|---|---|---|---|
str | Y | Y | Y | Y | N | |
int | Y | Y | Y | Y | N | |
bool | Y | Y | Y | Y | N | |
float | Y | Y | Y | Y | N | |
list/typing.List (typing.List isdeprecated) | ForJson, received as a JSON ListFor Query, can be received asvalue=1,2,3 iflist_disable_query_csv isFalse.For Form orQuery, received asvalue=1&value=2&value=3.A single value= with no value will always be transformed to an empty list, butvalue=, (Query only) andvalue=&value= will be transformed to a list of emptystr.Lists with None as an accepted type are only supported inJson parameters. | N | Y | Y | Y | N |
typing.Union | Y | Y | Y | Y | N | |
typing.Optional | Not supported forRoute inputs | Y | Y | Y | Y | Y |
datetime.datetime | Received as astr in ISO-8601 date-time format | Y | Y | Y | Y | N |
datetime.date | Received as astr in ISO-8601 full-date format | Y | Y | Y | Y | N |
datetime.time | Received as astr in ISO-8601 partial-time format | Y | Y | Y | Y | N |
dict | ForQuery andForm inputs, users should pass the stringified JSON | N | Y | Y | Y | N |
FileStorage | N | N | N | N | Y | |
A subclass ofStrEnum orIntEnum, or a subclass ofEnum withstr orint mixins prior to Python 3.11 | Y | Y | Y | Y | N | |
uuid.UUID | Received as astr with or without hyphens, case-insensitive | Y | Y | Y | Y | N |
These can be used in tandem to describe a parameter to validate:parameter_name: type_hint = ParameterSubclass()
parameter_name: The field name itself, such as usernametype_hint: The expected Python data typeParameterSubclass: An instance of a subclass ofParameter
Validation beyond type-checking can be done by passing arguments into the constructor of theParameter subclass. The arguments available for use on each type hint are:
| Parameter Name | Type of Argument | Effective On Types | Description |
|---|---|---|---|
default | any, exceptNoneType | All, except inRoute | Specifies the default value for the field, makes non-Optional fields not required |
min_str_length | int | str | Specifies the minimum character length for a string input |
max_str_length | int | str | Specifies the maximum character length for a string input |
min_list_length | int | list | Specifies the minimum number of elements in a list |
max_list_length | int | list | Specifies the maximum number of elements in a list |
min_int | int | int | Specifies the minimum number for an integer input |
max_int | int | int | Specifies the maximum number for an integer input |
whitelist | str | str | A string containing allowed characters for the value |
blacklist | str | str | A string containing forbidden characters for the value |
pattern | str | str | A regex pattern to test for string matches |
func | Callable[Any] -> Union[bool, tuple[bool, str]] | All | A function containing a fully customized logic to validate the value. See thecustom validation function below for usage |
datetime_format | str | datetime.datetime | Python datetime format string datetime format string (datetime format codes) |
comment | str | All | A string to display as the argument description in any generated documentation |
alias | str | All butFileStorage | An expected parameter name to receive instead of the function name. |
json_schema | dict | dict | An expectedJSON Schema which the dict input must conform to |
content_types | list[str] | FileStorage | AllowedContent-Types |
min_length | int | FileStorage | MinimumContent-Length for a file |
max_length | int | FileStorage | MaximumContent-Length for a file |
blank_none | bool | Optional[str] | IfTrue, an empty string will be converted toNone, defaults to configuredFPV_BLANK_NONE, seeValidation Behavior Configuration for more |
list_disable_query_csv | bool | list inQuery | IfFalse, list-type Query parameters will be split by,, defaults to configuredFPV_LIST_DISABLE_QUERY_CSV, seeValidation Behavior Configuration for more |
These validators are passed into theParameter subclass in the route function, such as:
username: str = Json(default="defaultusername", min_length=5)profile_picture: werkzeug.datastructures.FileStorage = File(content_types=["image/png", "image/jpeg"])filter: str = Query()
Custom validation functions passed into thefunc property can be used to validate an input against custom logic and return customized error responses for that validation
Example custom validation functions are below:
defis_even(val:int):"""Return a single bool, True if valid, False if invalid"""returnval%2==0defis_odd(val:int):"""Return a tuple with a bool, as above, and the error message if the bool is False"""returnval%2!=0,"val must be odd"
FPV_DOCS_SITE_NAME: str: Your site's name, to be displayed in the page title, default:SiteFPV_DOCS_CUSTOM_BLOCKS: array: An array of dicts to display as cards at the top of your documentation, with the (optional) keys:title: Optional[str]: The title of the cardbody: Optional[str] (HTML allowed): The body of the cardorder: int: The order in which to display this card (out of the other custom cards)
FPV_DOCS_DEFAULT_THEME: str: The default theme to display in the generated webpage
See theAPI Documentation below for other information on API Documentation generation
FPV_BLANK_NONE: bool: Set the defaultblank_nonebehavior for routes in your application, defaults toFalseif unsetFPV_LIST_DISABLE_QUERY_CSV: bool: Set the defaultlist_disable_query_csvbehavior for routes in your application, defaults toFalseif unset
Using the data provided through parameters, docstrings, and Flask route registrations, Flask Parameter Validation can generate API Documentation in various formats.To make this easy to use, it comes with aBlueprint and the output shown below and configuration optionsabove:
The documentation blueprint can be added using the following code:
fromflask_parameter_validation.docs_blueprintimportdocs_blueprint...app.register_blueprint(docs_blueprint)
The default blueprint adds twoGET routes:
/: HTML Page with Bootstrap CSS and toggleable light/dark mode/json: Non-standard Format JSON Representation of the generated documentation
The/json route yields a response with the following format:
{"custom_blocks":"<array entered in the FPV_DOCS_CUSTOM_BLOCKS config option, default: []>","default_theme":"<string entered in the FPV_DOCS_DEFAULT_THEME config option, default: 'light'>","docs":"<see get_route_docs() return value format below>","site_name":"<string entered in the FPV_DOCS_SITE_NAME config option, default: 'Site'"}Code:
@config_api.get("/")@ValidateParameters()defget_all_configs():""" Get the System Configuration Returns: <code>{"configs": [{"id": int, "module": str, "name": str, "description": str, "value": str}, ...] }</code> """system_configs= []forsystem_configinSystemConfig.query.all():system_configs.append(system_config.toDict())returnresp_success({"configs":system_configs})@config_api.post("/<int:config_id>")@ValidateParameters()defedit_config(config_id:int=Route(comment="The ID of the Config Record to Edit"),value:str=Json(max_str_length=2000,comment="The value to set in the Config Record")):"""Edit a specific System Configuration value"""config=SystemConfig.get_by_id(config_id)ifconfigisNone:returnresp_not_found("No link exists with ID "+str(config_id))else:config.update(value)returnresp_success()
Documentation Generated:
If you would like to use your own blueprint, you can get the raw data from the following function:
fromflask_parameter_validation.docs_blueprintimportget_route_docs...get_route_docs()
This method returns an object with the following structure:
[ {"rule":"/path/to/route","methods": ["HTTPVerb"],"docstring":"String, unsanitized of HTML Tags","decorators": ["@decorator1","@decorator2(param)"],"args": {"<Subclass of Parameter this route uses>": [ {"name":"Argument Name","type":"Argument Type","loc_args": {"<Name of argument passed to Parameter Subclass>":"Value passed to Argument","<Name of another argument passed to Parameter Subclass>":0 } } ],"<Another Subclass of Parameter this route uses>": [] } },...]An example of theJSON Schema validation is provided below:
json_schema= {"type":"object","required": ["user_id","first_name","last_name","tags"],"properties": {"user_id": {"type":"integer"},"first_name": {"type":"string"},"last_name": {"type":"string"},"tags": {"type":"array","items": {"type":"string"} } }}@api.get("/json_schema_example")@ValidateParameters()defjson_schema(data:dict=Json(json_schema=json_schema)):returnjsonify({"data":data})
Many thanks to all those who have made contributions to the project:
- d3-steichman/smt5541: API documentation, custom error handling, datetime validation and bug fixes
- summersz: Parameter aliases, async support, form type conversion and list bug fixes
- Garcel: Allow passing custom validator function
- iml1111: Implement regex validation
- borisowww: Fix file handling bugs
- Charlie-Mindified: Fix JSON handling bug
- dkassen: Helped to resolve broken list parsing logic
About
Get and validate all Flask input parameters with ease.
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Contributors11
Uh oh!
There was an error while loading.Please reload this page.
