In flask-restplus API , I need to make validation on request JSON data where I already defined request body schema with api.model
. Basically I want to pass input JSON data to API function where I have to validate input JSON data before using API function. To do so, I used RequestParser
for doing this task, but the API function was expecting proper JSON data as parameters after request JSON is validated and parsed. To do request JSON validation, first I have to parse received input JSON data, parse its JSON body, validate each then reconstructs it as JSON object, and pass to the API function. Is there any easier way to do this?
input JSON data
{
"body": {
"gender": "M",
"PCT": {
"value": 12,
"device": "roche_cobas"
},
"IL6": {
"value": 12,
"device": "roche_cobas"
},
"CRP": {
"value": 12,
"device": "roche_cobas"
}
}
}
my current attempt in flask
from flask_restplus import Api, Namespace, Resource, fields, reqparse, inputs
from flask import Flask, request, make_response, Response, jsonify
app = Flask(__name__)
api = Api(app)
ns = Namespace('')
feature = api.model('feature', {
'value': fields.Integer(required=True),
'time': fields.Date(required=True)
})
features = api.model('featureList', {
'age': fields.String,
'gender': fields.String(required=True),
'time': fields.Date,
'features': fields.List(fields.Nested(feature, required=True))
})
@ns.route('/hello')
class helloResource(Resource):
@ns.expect(features)
def post(self):
json_dict = request.json ## get input JSON data
## need to parse json_dict to validate expected argument in JSON body
root_parser = reqparse.RequestParser()
root_parser.add_argument('body', type=dict)
root_args = root_parser.parse_args()
jsbody_parser = reqparse.RequestParser()
jsbody_parser.add_argument('age', type=dict, location = ('body',))
jsbody_parser.add_argument('gender', type=dict, location=('body',))
## IL6, CRP could be something else, how to use **kwargs here
jsbody_parser.add_argument('IL6', type=dict, location=('body',))
jsbody_parser.add_argument('PCT', type=dict, location=('body',))
jsbody_parser.add_argument('CRP', type=dict, location=('body',))
jsbody_parser = jsbody_parser.parse_args(req=root_args)
## after validate each argument on input JSON request body, all needs to be constructed as JSON data
json_data = json.dumps(jsonify(jsbody_parser)) ## how can I get JSON again from jsbody_parser
func_output = my_funcs(json_data)
rest = make_response(jsonify(str(func_output)), 200)
return rest
if __name__ == '__main__':
api.add_namespace(ns)
app.run(debug=True)
update: dummy api function
Here is dummy function that expecting json data after validation:
import json
def my_funcs(json_data):
a =json.loads(json_data)
for k,v in a.iteritems():
print k,v
return jsonify(a)
current output of above attempt:
I have this on response body:
{
"errors": {
"gender": "'gender' is a required property"
},
"message": "Input payload validation failed"
}
obviously, request JSON input is not handled and not validated in my attempt. I think I have to pass json_dict
to RequestParser object, but still can't validate request JSON here. how to make this happen?
I have to validate expected arguments from JSON body, after validation, I want to construct JSON body that gonna be used as a parameter for API function. How can I make this happen? any workaround to achieve this?
parsed JSON must pass to my_funcs
in my post, request JSON data should be parsed, such as age
, gender
should be validated as expected arguments in the request JSON, then jsonify added arguments as JSON and pass the my_funcs
. how to make this happen easily in fl
I want to make sure flask should parse JSON body and add arguments as it expected, otherwise throw up error. for example:
{
"body": {
"car": "M",
"PCT": {
"value": 12,
"device": "roche_cobas"
},
"IL6": {
"device": "roche_cobas"
},
"CRP": {
"value": 12
}
}
}
if I give JSON data like above for making POST request to a server endpoint, it should give the error. How to make this happen? how to validate POST request JSON for flask API call?
As I tried to convey in our conversation it appears you are after a serialization and deserialization tool. I have found Marshmallow to be an exceptional tool for this (it is not the only one). Here's a working example of using Marshmallow to validate a request body, converting the validated data back to a JSON string and passing it to a function for manipulation, and returning a response with JSON data:
from json import dumps, loads
from flask import Flask, jsonify, request
from marshmallow import Schema, fields, ValidationError
class BaseSchema(Schema):
age = fields.Integer(required=True)
gender = fields.String(required=True)
class ExtendedSchema(BaseSchema):
# have a look at the examples in the Marshmallow docs for more complex data structures, such as nested fields.
IL6 = fields.String()
PCT = fields.String()
CRP = fields.String()
def my_func(json_str:str):
""" Your Function that Requires JSON string"""
a_dict = loads(json_str)
return a_dict
app = Flask(__name__)
@app.route('/base', methods=["POST"])
def base():
# Get Request body from JSON
request_data = request.json
schema = BaseSchema()
try:
# Validate request body against schema data types
result = schema.load(request_data)
except ValidationError as err:
# Return a nice message if validation fails
return jsonify(err.messages), 400
# Convert request body back to JSON str
data_now_json_str = dumps(result)
response_data = my_func(data_now_json_str)
# Send data back as JSON
return jsonify(response_data), 200
@app.route('/extended', methods=["POST"])
def extended():
""" Same as base function but with more fields in Schema """
request_data = request.json
schema = ExtendedSchema()
try:
result = schema.load(request_data)
except ValidationError as err:
return jsonify(err.messages), 400
data_now_json_str = dumps(result)
response_data = my_func(data_now_json_str)
return jsonify(response_data), 200
Here's some quick tests to show validation, as well as extending the fields in your request body:
import requests
# Request fails validation
base_data = {
'age': 42,
}
r1 = requests.post('http://127.0.0.1:5000/base', json=base_data)
print(r1.content)
# Request passes validation
base_data = {
'age': 42,
'gender': 'hobbit'
}
r2 = requests.post('http://127.0.0.1:5000/base', json=base_data)
print(r2.content)
# Request with extended properties
extended_data = {
'age': 42,
'gender': 'hobbit',
'IL6': 'Five',
'PCT': 'Four',
'CRP': 'Three'}
r3 = requests.post('http://127.0.0.1:5000/extended', json=extended_data)
print(r3.content)
Hope this help gets you where you're going.