由于微信h5鱼虾蟹棋牌网站开发平台Q1687054422(h5.fanshubbs.com)的原因,至今仍有大量生产环境的代码跑在 Python 2.7 之上,在 Python 2 的世界里,并没有一个官方的类型系统实现。那么生产环境的类型系统是如何实现的呢,为什么一定要在在线服务上实现类型系统?下文将针对这两个问题进行深入讨论。
什么是 API 的类型系统
人们常说一门编程语言的类型系统,通常指一门编程语言在表达式的类型意义上所具有的表达能力。而对于 API 来说,对于其输入(参数)和输出(响应)都能够有完善的类型表达,那么就可以认为它具有了基本的类型系统。
一个包含了方法(Method/HTTP verb)和路径(Path)的 API,常常称之为一个访问点(endpoint)或 API,每一个 API 具有一个描述性质的声明,称之为 Schema,Schema 可以有多种定义方式,但至少会包含参数(请求字段及其类型定义)和响应(状态码,响应字段及类型定义)。比较典型的是 OpenAPI 规范的定义,该规范将在下文详细介绍。
那么在线服务上实现类型系统有何意义?如果一个 API Framework 或者 RPC Remote Call 没有类型系统,会出现什么样的问题呢?
为什么要在在线服务上实现类型系统
本文认为在线服务上的类型系统至少有以下几种直接的作用:
- 验证参数的可靠性,由于在服务开发时,不能信任用户的输入,应做好最坏的假设,就如同墨菲在静静地看着你。
- 自动生成文档和超文本链接,一个完善的 Schema 系统,可以为 HATEOAS(Hypertext As The Engine Of Application State) 提供支持。
- 自动生成 Definition 文件(比如 thrift,protobuf 等 RPC 定义),用于在服务端提供兼容多种协议的网关,在客户端为终端用户提供本地验证机制。
- 和异常系统结合,可以为异常诊断和 Traceback 提供支持,使用更有针对性的诊断方式。
- 可以和接口测试相结合,推断返回值的类型(但 Python 2 的库实现比较庞杂,很难实现这一点)。
安全性和可解释性
API 类型系统的作用,最终可以总结为在 「 安全性 」和「 可解释性」 上的提升。
如果没有一个一致的类型系统,往往要使用大量冗余代码(自定义函数)来进行参数校验,而非通过自定义类型来验证。并且耗费大量的精力人工编写接口文档,在接口变更后还要人工修改和校对。
在类型系统中,安全性和可解释性是互相依存的关系,仅从安全性考虑,如果代码结构合理,使用自定义函数进行参数校验也是可以接受的,但函数在可解释性上是弱于类型系统的,对于接口附加的元信息(比如参数类型,参数是否可选,参数描述)难以自然地表述。
类型系统在提升了安全性的同时,还兼顾了系统的可解释性,这是在服务治理上非常需要的一点。
类型系统实践
下面以 Python 2.7 为例,详细介绍下如何在一个在线服务上实现类型系统,以及类型系统可以帮助研发人员做哪些有意义的事情。
marshmallow
Python 2 中没有一个官方的类型系统实现,所以在 API 参数的验证中,往往是通过外挂第三方 Schema 实现的。
marshmallow 是本文选用的一个对类型系统进行建模的 Python 库,它有着极高的流行程度,提供了基本的类型定义、参数验证功能和序列化 / 反序列化机制。
现在假设研发团队要开发一个用户相关的接口,首先要对用户这个服务资源进行抽象定义,一个基本的 Schema 定义如下:
清单 1. 一个用户接口参数模式定义
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# -*- coding: utf-8 -*-
import re
from marshmallow import Schema, fields, validate
from myapp import fields as myfields
class UserSchema(Schema):
user_id = myfields.UserId(required=True, help=u'用户的唯一 ID')
nickname = fields.Str(required=True,
validate=validate.Length(min=2, max=20),
help=u'用户的昵称')
email = fields.Email(required=True, u'用户的邮箱,不可重复')
|
marshmallow 自带了许多内建类型,比如 Email,URL,UUID 等,研发人员也可以根据业务来定制自定义类型,比如上文的 UserId 可以像这样定义:
清单 2. 自定义类型示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
|