烧瓶:如何对请求JSON和JSON模式进行验证?

时间:2020-05-06 20:06:56

标签: python json flask

在flask-restplus API中,我需要对已经使用api.model定义了请求主体架构的请求JSON数据进行验证。基本上,我想将输入的JSON数据传递给API函数,在使用API​​函数之前,我必须先验证输入的JSON数据。为此,我使用RequestParser来完成此任务,但是在验证和解析了请求JSON之后,API函数期望使用正确的JSON数据作为参数。要请求JSON验证,首先我必须解析接收到的输入JSON数据,解析其JSON主体,进行验证,然后将其重构为JSON对象,并传递给API函数。有没有更简单的方法可以做到这一点?

输入JSON数据

{
  "body": {
    "gender": "M",
    "PCT": {
      "value": 12,
      "device": "roche_cobas"
    },
    "IL6": {
      "value": 12,
      "device": "roche_cobas"
    },
    "CRP": {
      "value": 12,
      "device": "roche_cobas"
    }
  }
}

我目前在烧瓶中的尝试

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)

更新:虚拟api函数

这里是伪函数,在验证后需要json数据:

import json
def my_funcs(json_data):
    a =json.loads(json_data)
    for k,v in a.iteritems():
           print k,v
    return jsonify(a)

上述尝试的当前输出

我在响应正文中有此提示

{
  "errors": {
    "gender": "'gender' is a required property"
  },
  "message": "Input payload validation failed"
}

很明显,请求JSON输入在我的尝试中未得到处理和验证。我认为我必须将json_dict传递给RequestParser对象,但仍然无法在此处验证请求JSON。如何做到这一点?

我必须从JSON主体验证期望的参数,验证之后,我想构造要用作API函数参数的JSON主体。我怎样才能做到这一点?任何解决此问题的方法?

解析的JSON必须传递给my_funcs

在我的帖子中,应解析请求JSON数据,例如age,应将gender验证为请求JSON中的预期参数,然后将添加的参数作为JSON进行JSON验证并传递{{1 }}。如何在fl中轻松实现这一点

我想确保flask应该解析JSON主体并按预期添加参数,否则抛出错误。例如:

my_funcs

如果我像上面那样提供JSON数据以向服务器端点发出POST请求,它应该给出错误。如何做到这一点?如何验证烧瓶API调用的POST请求JSON?

1 个答案:

答案 0 :(得分:2)

当我试图在我们的对话中传达信息时,您似乎正在使用序列化和反序列化工具。我发现棉花糖是解决这个问题的出色工具(不是唯一的工具)。这是一个使用棉花糖验证请求主体,将验证后的数据转换回JSON字符串并将其传递给函数进行操作以及返回带有JSON数据的响应的工作示例:

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

这里有一些快速测试来显示验证,以及扩展请求正文中的字段:

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)

希望这可以帮助您到达目的地。