Skip to content

Commit abcb480

Browse files
committed
Update and reorganize README
1 parent 7b99414 commit abcb480

File tree

1 file changed

+108
-73
lines changed

1 file changed

+108
-73
lines changed

README.md

+108-73
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
- `git clone https://github.com/Ge0rg3/flask-parameter-validation.git`
88
- `python setup.py install`
99

10-
11-
## Simple Usage
10+
## Usage Example
1211
```py
1312
from flask import Flask
1413
from typing import List, Optional
@@ -17,7 +16,6 @@ from datetime import datetime
1716

1817
app = Flask(__name__)
1918

20-
2119
@app.route("/update/<int:id>", methods=["POST"])
2220
@ValidateParameters()
2321
def hello(
@@ -37,70 +35,21 @@ if __name__ == "__main__":
3735
app.run()
3836
```
3937

40-
## Detailed Usage
41-
1. We use the ValidateParameters decorator on all functions that this modules should be used in.
42-
2. The format for arguments is as follows:
43-
`parameter_name: parameter_type = Class()`
44-
In this example, `parameter_name` would be the field name itself, such as "username". `parameter_type` would be the expected python data type, such as str, int, List, Union etc. Finally, `Class()` is one of the class inputs, as detailed below:
45-
46-
### Classes
47-
1. Route()
48-
This is the data passed through the URL, such as `/users/<int:id>`
49-
2. Form()
50-
This is the data passed by a normal HTML form, often with x-www-form-urlencoded content-type.
51-
3. Json()
52-
This is any JSON body sent -- request must have application/json content type for flask to read this.
53-
4. Query()
54-
This covers query parameters (aka GET parameters), such as `/news/article?id=55`
55-
5. File()
56-
The validation on files are different to the others, but file input can still be obtained here as their Flask FileStorage objects.
57-
58-
### Input types
59-
* str
60-
* int
61-
* bool
62-
* float
63-
* typing.List (must use this, not just `list`)
64-
* typing.Union
65-
* typing.Optional
66-
* datetime.datetime
67-
* datetime.date
68-
* datetime.time
69-
* dict
70-
71-
### Validation
72-
All parameters can have default values, and automatic validation.
73-
`Route`, `Form`, `Json` and `Query` have the following options:
74-
* default: any, Specifies the default value for the field.
75-
* min_str_length: int, Specifies the minimum character length for a string input
76-
* max_str_length: int, Specifies the maximum character length for a string input
77-
* min_list_length: int, Specifies the minimum number of elements in a list
78-
* max_list_length: int, Specifies the maximum number of elements in a list
79-
* min_int: int, Specifies the minimum number for an int input
80-
* max_int: int, Specifies the maximum number for an int input
81-
* whitelist: str, A string containing allowed characters for the value
82-
* blacklist: str, A string containing forbidden characters for the value
83-
* pattern: str, A regex pattern to test for string matches
84-
* func: Callable -> Union[bool, tuple[bool, str]], A function containing a fully customized logic to validate the value
85-
* datetime_format: str, datetime format string ([datetime format codes](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes))
86-
* comment: str, A string to display as the argument description in generated documentation (if used)
87-
* alias: str, An expected parameter name instead of the function name. See `access_type` example for clarification.
88-
* json_schema: dict, An expected [JSON Schema](https://json-schema.org) which the dict input must conform to
89-
90-
`File` has the following options:
91-
* content_types: array of strings, an array of allowed content types.
92-
* min_length: Minimum content-length for a file
93-
* max_length: Maximum content-length for a file
94-
95-
These validators are passed into the classes in the route function, such as:
96-
* `username: str = Json("defaultusername", min_length=5)`
97-
* `profile_picture: werkzeug.datastructures.FileStorage = File(content_types=["image/png", "image/jpeg"])`
98-
* `filter: str = Query()`
38+
## Usage
39+
To validate parameters with flask-parameter-validation, two conditions must be met.
40+
1. The `@ValidateParameters()` decorator must be applied to the function
41+
2. Type hints ([supported types](#type-hints-and-accepted-input-types)) and a default of a subclass of `Parameter` for the parameters you want to use flask-parameter-validation on
42+
9943

100-
Note: For `typing.List` Query inputs, users can pass via either `value=1&value=2&value=3`, or `value=1,2,3`. Both will be transformed to a list.
44+
### Enable and customize Validation for a Route with the @ValidateParameters decorator
45+
The `@ValidateParameters()` decorator takes parameters that alter route validation behavior or provide documentation information:
10146

102-
### Overwriting default errors
103-
By default, the error messages are returned as a JSON response, with the detailed error in the "error" field. However, this can be edited by passing a custom error function into the ValidateParameters decorator. For example:
47+
| Parameter | Type | Default | Description |
48+
|-------------------|----------------------|---------|------------------------------------------------------------------------------------------------------------------------------|
49+
| error_handler | `Optional[Response]` | `None` | Overwrite the output format of generated errors, see [Overwriting Default Errors](#overwriting-default-errors) for more |
50+
51+
#### Overwriting Default Errors
52+
By default, the error messages are returned as a JSON response, with the detailed error in the "error" field. However, this can be edited by passing a custom error function into the `ValidateParameters()` decorator. For example:
10453
```py
10554
def error_handler(err):
10655
error_name = type(err)
@@ -113,11 +62,94 @@ def error_handler(err):
11362
}, 400
11463

11564
@ValidateParameters(error_handler)
65+
@app.route(...)
11666
def api(...)
11767
```
11868

69+
### Specify Parameter types and constraints with type hints and subclasses of Parameter
70+
#### Parameter Class
71+
The `Parameter` class provides a base for validation common among all input types, all location-specific classes extend `Parameter`. These subclasses are:
72+
73+
| Subclass Name | Input Source | Available For |
74+
|---------------|------------------------------------------------------------------------------------------------------------------------|------------------|
75+
| Route | Parameter passed in the pathname of the URL, such as `/users/<int:id>` | All HTTP Methods |
76+
| Form | Parameter in an HTML form or a `FormData` object in the request body, often with `Content-Type: x-www-form-urlencoded` | POST Methods |
77+
| Json | Parameter in the JSON object in the request body, must have header `Content-Type: application/json` | POST Method |
78+
| Query | Parameter in the query of the URL, such as /news_article?id=55 | All HTTP Methods |
79+
| File | Parameter is a file uploaded in the request body | POST Method |
80+
81+
#### Type Hints and Accepted Input Types
82+
Type Hints allow for inline specification of the input type of a parameter. Some types are only available to certain `Parameter` subclasses.
83+
84+
| Type Hint / Expected Python Type | Notes | `Route` | `Form` | `Json` | `Query` | `File` |
85+
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|---------|--------|--------|---------|--------|
86+
| `str` | | Y | Y | Y | Y | N |
87+
| `int` | | Y | Y | Y | Y | N |
88+
| `bool` | | Y | Y | Y | Y | N |
89+
| `float` | | Y | Y | Y | Y | N |
90+
| `typing.List` (must not be `list`) | For `Query` inputs, users can pass via either `value=1&value=2&value=3`, or `value=1,2,3`, both will be transformed to a `list`. | N | Y | Y | Y | N |
91+
| `typing.Union` | | Y | Y | Y | Y | N |
92+
| `typing.Optional` | | Y | Y | Y | Y | Y |
93+
| `datetime.datetime` | received as a `str` in ISO-8601 date-time format | Y | Y | Y | Y | N |
94+
| `datetime.date` | received as a `str` in ISO-8601 full-date format | Y | Y | Y | Y | N |
95+
| `datetime.time` | received as a `str` in ISO-8601 partial-time format | Y | Y | Y | Y | N |
96+
| `dict` | | N | N | Y | N | N |
97+
| `FileStorage` | | N | N | N | N | Y |
98+
99+
These can be used in tandem to describe a parameter to validate: `parameter_name: type_hint = ParameterSubclass()`
100+
- `parameter_name`: The field name itself, such as username
101+
- `type_hint`: The expected Python data type
102+
- `ParameterSubclass`: An instance of a subclass of `Parameter`
103+
104+
### Validation with arguments to Parameter
105+
Validation beyond type-checking can be done by passing arguments into the constructor of the `Parameter` subclass. The arguments available for use on each type hint are:
106+
107+
| Parameter Name | Type of Parameter | Effective On Types | Description |
108+
|-------------------|---------------------------------------------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
109+
| `default` | any | All | Specifies the default value for the field, makes non-Optional fields not required |
110+
| `min_str_length` | `int` | `str` | Specifies the minimum character length for a string input |
111+
| `max_str_length` | `int` | `str` | Specifies the maximum character length for a string input |
112+
| `min_list_length` | `int` | `typing.List` | Specifies the minimum number of elements in a list |
113+
| `max_list_length` | `int` | `typing.List` | Specifies the maximum number of elements in a list |
114+
| `min_int` | `int` | `int` | Specifies the minimum number for an integer input |
115+
| `max_int` | `int` | `int` | Specifies the maximum number for an integer input |
116+
| `whitelist` | `str` | `str` | A string containing allowed characters for the value |
117+
| `blacklist` | `str` | `str` | A string containing forbidden characters for the value |
118+
| `pattern` | `str` | `str` | A regex pattern to test for string matches |
119+
| `func` | `Callable -> Union[bool, tuple[bool, str]]` | All | A function containing a fully customized logic to validate the value. See the [custom validation function](#custom-validation-function) below for usage |
120+
| `datetime_format` | `str` | `datetime.datetime` | Python datetime format string datetime format string ([datetime format codes](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes)) |
121+
| `comment` | `str` | All | A string to display as the argument description in any generated documentation |
122+
| `alias` | `str` | All but `FileStorage` | An expected parameter name to receive instead of the function name. |
123+
| `json_schema` | `dict` | `dict` | An expected [JSON Schema](https://json-schema.org) which the dict input must conform to |
124+
| `content_types` | `list[str]` | `FileStorage` | Allowed `Content-Type`s |
125+
| `min_length` | `int` | `FileStorage` | Minimum `Content-Length` for a file |
126+
| `max_length` | `int` | `FileStorage` | Maximum `Content-Length` for a file |
127+
128+
These validators are passed into the `Parameter` subclass in the route function, such as:
129+
* `username: str = Json(default="defaultusername", min_length=5)`
130+
* `profile_picture: werkzeug.datastructures.FileStorage = File(content_types=["image/png", "image/jpeg"])`
131+
* `filter: str = Query()`
132+
133+
#### Custom Validation Function
134+
135+
Custom validation functions passed into the `func` property can be used to validate an input against custom logic and return customized error responses for that validation
136+
137+
Example custom validation functions are below:
138+
```py
139+
def is_even(val: int):
140+
"""Return a single bool, True if valid, False if invalid"""
141+
return val % 2 == 0
142+
143+
def is_odd(val: int):
144+
"""Return a tuple with a bool, as above, and the error message if the bool is False"""
145+
return val % 2 != 0, "val must be odd"
146+
```
147+
119148
### API Documentation
120-
Using the data provided through parameters, docstrings, and Flask route registrations, Flask Parameter Validation can generate an API Documentation page. To make this easy to use, it comes with a blueprint and the configuration options below:
149+
Using the data provided through parameters, docstrings, and Flask route registrations, Flask Parameter Validation can generate API Documentation in various formats.
150+
To make this easy to use, it comes with a `Blueprint` and the output and configuration options below:
151+
152+
#### Format
121153
* `FPV_DOCS_SITE_NAME: str`: Your site's name, to be displayed in the page title, default: `Site`
122154
* `FPV_DOCS_CUSTOM_BLOCKS: array`: An array of dicts to display as cards at the top of your documentation, with the (optional) keys:
123155
* `title: Optional[str]`: The title of the card
@@ -135,14 +167,14 @@ app.register_blueprint(docs_blueprint)
135167

136168
The default blueprint adds two `GET` routes:
137169
* `/`: HTML Page with Bootstrap CSS and toggleable light/dark mode
138-
* `/json`: JSON Representation of the generated documentation
170+
* `/json`: Non-standard Format JSON Representation of the generated documentation
139171

140172
The `/json` route yields a response with the following format:
141173
```json
142174
{
143175
"custom_blocks": "<array entered in the FPV_DOCS_CUSTOM_BLOCKS config option, default: []>",
144176
"default_theme": "<string entered in the FPV_DOCS_DEFAULT_THEME config option, default: 'light'>",
145-
"docs": "<see get_docs_arr() return value format below>",
177+
"docs": "<see get_route_docs() return value format below>",
146178
"site_name": "<string entered in the FPV_DOCS_SITE_NAME config option, default: 'Site'"
147179
}
148180
```
@@ -188,15 +220,15 @@ Documentation Generated:
188220

189221
![](docs/api_documentation_example.png)
190222

191-
#### Custom Blueprint
223+
##### Custom Blueprint
192224
If you would like to use your own blueprint, you can get the raw data from the following function:
193225
```py
194-
from flask_parameter_validation.docs_blueprint import get_docs_arr
226+
from flask_parameter_validation.docs_blueprint import get_route_docs
195227
...
196-
get_docs_arr()
228+
get_route_docs()
197229
```
198230

199-
##### get_docs_arr() return value format
231+
###### get_route_docs() return value format
200232
This method returns an object with the following structure:
201233

202234
```json
@@ -205,6 +237,7 @@ This method returns an object with the following structure:
205237
"rule": "/path/to/route",
206238
"methods": ["HTTPVerb"],
207239
"docstring": "String, unsanitized of HTML Tags",
240+
"decorators": ["@decorator1", "@decorator2(param)"],
208241
"args": {
209242
"<Subclass of Parameter this route uses>": [
210243
{
@@ -219,10 +252,12 @@ This method returns an object with the following structure:
219252
"<Another Subclass of Parameter this route uses>": []
220253
}
221254
},
255+
222256
...
223257
]
224258
```
225259

260+
226261
### JSON Schema Validation
227262
An example of the [JSON Schema](https://json-schema.org) validation is provided below:
228263
```python
@@ -248,7 +283,7 @@ def json_schema(data: dict = Json(json_schema=json_schema)):
248283

249284
## Contributions
250285
Many thanks to all those who have made contributions to the project:
251-
* [d3-steichman](https://github.com/d3-steichman): API documentation, custom error handling, datetime validation and bug fixes
286+
* [d3-steichman](https://github.com/d3-steichman)/[smt5541](https://github.com/smt5541): API documentation, custom error handling, datetime validation and bug fixes
252287
* [summersz](https://github.com/summersz): Parameter aliases, async support, form type conversion and list bug fixes
253288
* [Garcel](https://github.com/Garcel): Allow passing custom validator function
254289
* [iml1111](https://github.com/iml1111): Implement regex validation

0 commit comments

Comments
 (0)