JSON Verification

One of the uses of the flask_verify package is to make sure routes correctly require JSON requests or respond with JSON responses. This is done using simple decorators, this way, we need not worry about verifying our requests or converting our return types in each route function.

Verifying Requests

Imagine a function such as:

@app.route('/example_route', methods=["POST"])
def example_view():
    if not request.json and any(key not in request.json for key in ('message', 'data')):
        return {'error': 'Not JSON'}, 400
    message = request.json['message']
    return {'message': message}, 200

Now, despite taking very small space, it is cumbersome to copy and paste the lines checking whether or not our request contains JSON and specific keys. Furthermore, it looks confusing and hard to read at a glance. We can simplify this by using our decorator flask_verify.verify_json.verify_json_request(), the same code snippet can be written as:

@app.route('/example_route')
@verify_json_request(must_contain=('message', 'data'))
def example_view():
    message = request.json['message']
    return {'message': message}, 200

Looks much simpler and much easier to read.

Verifying Responses

Flask route functions that return compatible return types can automatically be converted without having to convert them to JSON by hand. Normal Flask supports this with dictionaries, but flask_verify also supports:

  1. Any datatype that can be converted to a JSON string with dumps. Such as list.

  2. Dataclass objects. Their fields and values are automatically converted into a dict and then to a JSON string.

Consider the following:

@dataclass
class ExampleClass:
    a: str
    b: str

@app.route('/example_route')
def example_view():
    return jsonify(["hello", "world"]), 200

@app.route('/dataclass_route')
def dataclass_view():
    dataclass_ = ExampleClass("Hello", "world")
    return asdict(dataclass_), 200

Using flask_verify.verify_json.verify_json_response(), we can convert these two routes into a simpler form:

@dataclass
class ExampleClass:
    a: str
    b: str

@app.route('/example_route')
@verify_json_response()
def example_view():
    return ["hello", "world"], 200

@app.route('/dataclass_route')
@verify_json_response()
def dataclass_view():
    dataclass_ = ExampleClass("Hello", "world")
    return dataclass_, 200

Combining Response and Request Verification

In many cases, you will find yourself having Python functions that receive JSON requests and send JSON responses at the same time, rather than writing our two decorators one after the another, you can actually combine them. For instance, imagine a use case where we want to take a user id from the request body, then we will fetch a UserInfo object which is a dataclass and we want to return it as JSON, this can all be done simply as:

@app.route('/get_info', methods=["POST"])
@verify_json_route(must_contain=('user_id',))
def get_user_info():
    user_info = get_user_info(request.json['user_id'])
    return user_info, 200