服务端json参数校验神器Json Schema

目录

      • 目录
      • json简介
      • 服务端校验参数需求分析
      • json参数检验简单而繁琐方式
      • Json Schema
        • Json Schema 入门
      • Json Schema 表达式
        • string
        • Numeric types
        • object
        • array
        • boolean
        • null
      • 实战演习
      • 总结
      • 原文地址
      • 参考文献

json简介

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。
举个栗子:

{ "an": [ "arbitrarily", "nested" ], "data": "structure" }

服务端校验参数需求分析

参数检测是服务器端拿到参数的第一步操作,后续的一系列操作都完全信任数据的可靠安全性。可以准确的说,前端的参数校验是为了适当的保护服务端,为了更好的用户体验,但服务端万万不可信任前端传递的参数,所有的参数一定要进行校验,确保输入数据是合法正确性,这是服务端程序员的基本准则。常见的参数检验包括非空,最大长度检测,大小检测等等。应用在执行业务逻辑之前,必须通过校验保证接受到的输入数据是合法正确的,但很多时候同样的校验出现了多次,在不同的层,不同的方法上,导致代码冗余,浪费时间。

json参数检验简单而繁琐方式

可以针对每个字段,进行一系列的参数校验,然后完成该json参数的检验。
举个栗子:

dict = { "an": [ "arbitrarily", "nested" ], "data": "structure" }
if dict.get(data, '')=='':
    return 'key data is no null'
if len(dict.get(data, ''))>30:
    return "data is too long"   
other verify

就是这样不断的针对每一个字段进行校验,确保输入的数据的可靠、安全性。
缺点:很多时候同样的校验出现了多次,在不同的层,不同的方法上,导致代码冗余,浪费时间。比如校验是否为空,检验长度对大等等大量重复代码,效率很低。

Json Schema

相信用过flask-wtf的同学都会很喜欢这样的校验工具,自动对每个字段进行,而且效率还很高,不用写大量的重复代码,开发效率会得到大大的提升。反正我在用html的表单提交时都会使用flask-wtf,只能说,你用过一次就会爱上它。
那么当前端提交的数据不是form而是json时怎么办呢?如果不想像上面一样每个字段或者每个数据都一一检测,这时候你可以尝试使用json schema,很像flask-wtf的一个开源框架,专门用来校验json或者可以转换成json的dict。

Json Schema 入门

先举个栗子,大致说一下json schema怎么用

>>> from jsonschema import validate

>>> # A sample schema, like what we'd get from json.load()
>>> schema = {
...     "type" : "object",
...     "properties" : {
...         "price" : {"type" : "number"},
...         "name" : {"type" : "string"},
...     },
... }

>>> # If no exception is raised by validate(), the instance is valid.
>>> validate({"name" : "Eggs", "price" : 34.99}, schema)

>>> validate(
...     {"name" : "Eggs", "price" : "Invalid"}, schema
... )                                   # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...i
ValidationError: 'Invalid' is not of type 'number'

你可以使用validate来验证输入的json是否符合你需要的模式,如果不符合要求会抛出ValidationError异常,ValidationError.message 会提示不符合哪个要求或哪个要求出错,ValidationError.instance会提示是不符合该要求的字段的内容或者实例是什么。具体的schema 的表达式该怎么写后面会介绍到。

Json Schema 表达式

json schema的表达式在这里就讲点基本的好了,其实基本的已经够用了,如果要想更深入学习,可以到官网json schema进行学习。
json schema能支持的类型有string,Numeric types,object,array,boolean,null。接下来会一一解释。

string

举个例子来说明:

{
  "type": "string",
  "minLength": 2,
  "maxLength": 3
}
'A' is wrong,  'aa' is ok, 'aaa' is ok ,'aaaasf' is wrong

这表明它需要一个string字段,其中string 有的属性有:

  • minLength
  • maxLength
  • pattern (正则表达式验证)
  • format (目前支持:”date-time”,”email”,”hostname”,”ipv4”,”ipv6”,”uri”, 如果没有你需要的格式,你可以用pattern 的正则表达式来完成)

Numeric types

老惯例,上来举个例子:

{
  "type": "number",
  "minimum": 0,
  "maximum": 100,
  "exclusiveMaximum": true
}
ok:
0
10
99
wrong:
-1
100
101

这表明它需要一个Numeric types字段,其中number有的属性有:

  • type(integer,number)
  • multipleOf(要求该数字是这个数的整数倍)
  • minimum 、exclusiveMinimum 、maximum 、exclusiveMaximum (最大值、最小值以及是否包含该最大最小值)

object

在Python中,object类似于dict类型,”type”:”object”就是声明需要dict类。举个例子,就能大致理解了:

{
  "type": "object",
  "properties": {
    "number":      { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": { "type": "string",
                     "enum": ["Street", "Avenue", "Boulevard"]
                   }
  }
}
ok:
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" }
{ "number": 1600, "street_name": "Pennsylvania" }
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }
wrong:
{ "number": "1600", "street_name": "Pennsylvania", "street_type": "Avenue" }

这表明它需要dict的类型。object具有的属性有:

  • additionalProperties: 是否可以需要多其它的字典关键字。例如”additionalProperties”: { “type”: “string” }表明允许多的关键字必须是string类型,additionalProperties:false则表明不允许多其它的关键字。
  • required:表明必须要有该关键字,例如 “required”: [“number”, “street_name”] 必须要有这number和street_name这两个key。
  • minProperties,maxProperties 表明这个字典最少、最多拥有多少个keys。
  • dependencies:依赖属性,例如 “dependencies”: { “credit_card”:[“billing_address”]}表明有credit_card是必须有billing_address这个key。
  • patternProperties:key为正则表达式。例如”patternProperties”: {“^S_”: { “type”: “string” }},需要S_开头key

    array


    array对应python里的[],可以是列表也可以是原组,举个例子:
{
  "type": "array",
  "items": [
    {
      "type": "number"
    },
    {
      "type": "string"
    },
    {
      "type": "string",
      "enum": ["Street", "Avenue", "Boulevard"]
    },
    {
      "type": "string",
      "enum": ["NW", "NE", "SW", "SE"]
    }
  ]
}
则
[1600, "Pennsylvania", "Avenue", "NW"] is ok

“Drive” is not one of the acceptable street types:
[24, "Sussex", "Drive"] is wrong

This address is missing a street number 
["Palais de l'Élysée"] is wrong

It’s okay to not provide all of the items:
[10, "Downing", "Street"] is ok

And, by default, it’s also okay to add additional items to end:
[1600, "Pennsylvania", "Avenue", "NW", "Washington"] is ok

item具有的属性是:
- additionalItems :该additionalItems关键字控制是否有效有超出了所定义的数组中的其他项目items。在这里,我们将重用上面的示例模式,但设置 additionalItems为false,这会导致不允许数组中的额外项。
- minItems、maxItems:minItems和 maxItems关键字指定数组的长度。每个关键字的值必须是非负数。
- Uniqueness:模式可以确保数组中的每个项都是唯一的。只需将uniqueItems关键字设置为true。

boolean


在Python中,“boolean”类似于bool。请注意,在JSON中, true它false是小写的,而在Python中它们是大写的(True和False)。
举个例子:

{ "type": "boolean" }
OK:
true
false
wrong:
"true"
0

布尔类型只匹配两个特殊值:true和 false。请注意,架构不接受评估为true或的值false,例如1和0

null


在Python中,null类似于None。

{ "type": "null" }
ok:
null
wrong:
false
0
""

null类型通常用于表示缺失值。当模式指定a type时null,它只有一个可接受的值:null

实战演习

USERS_SCHEMA = {
    "type": "object",
    "required": ["users"],
    "properties": {
        "users": {
            "type": "array",
            "items": {
                "type": "object",
                "required": ["name", "email"],
                "properties": {
                    "name": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 30,
                    },
                    "primary_sector": {
                        "type": "string",
                        "maxLength": 60,
                    },
                    "second_sector": {
                        "type": "string",
                        "maxLength": 60,
                    },
                    "tertiary_sector": {
                        "type": "string",
                        "maxLength": 60,
                    },
                    "email": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 60,
                        "pattern":  '^([a-zA-Z0-9]+[-_.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[-_.]?)*'\
                                    '[a-zA-Z0-9]+\.[a-zA-Z]{2,6}$',
                    }
                },
            },
        },
    },
}

用来对应的符合要求的json是

{
    "users":{
        [{
            "name": "hello",
            "primary_sector": "一级部门",
            "second_sector": "二级部门",
            "tertiary_sector": "三级部门",
            "email" : "邮箱",
        }],
        [{
            "name": "hello1",
            "primary_sector": "一级部门",
            "second_sector": "二级部门",
            "tertiary_sector": "三级部门",
            "email" : "邮箱",
        }],
    }
}

可以一次性验证json里含有多有输入用户资料的json,也可以验证一个或者多个user。

总结

Json Schema给人感觉比较像在写json正则表达式,按着基本的帮助文档和知道,通过组合基本就能写出你需要的json验证格式,然后验证的时候只需要一句validate()就可以了,如果验证失败会抛出异常。建议以后验证json格式还是习惯使用这种验证方式,会大大提高开发效率。
上面是我的一些个人理解和看法,如果有错误,请多指教。

原文地址

原文地址:https://blog.csdn.net/aizenggege/article/details/82709790

参考文献

https://json-schema.org/understanding-json-schema/reference/null.html
http://imweb.io/topic/56b1b4bb5c49f9d377ed8ee9
http://taobaofed.org/blog/2016/01/25/jsonschema/
https://cloud.tencent.com/developer/article/1005810

你可能感兴趣的:(python,Web开发)