🌵api参数模型
先看一下正常情况下,一个简单接口的接口定义:
class Instance(BaseApi):
def get_instances(self):
http_data = {
"owner": self.owner,
"action": "DescribeInstances",
"reverse": 1,
"status": ["pending", "running", "stopped", "suspended", "rescuing"],
}
resp = self.send_http(http_data)
return resp
可以看到,这个接口的参数其实很简单,这么写是ok的。
但是如果一个复杂的接口,可能会有几十上百个参数,比如:
class Instance(BaseApi):
def run_instance(self, 入参可能太多省略...):
body = {
"image_id": image_id,
"instance_type": instance_type,
"cpu": cpu,
"memory": memory,
"instance_name": instance_name,
"count": count,
"vxnets": vxnets,
"nics": nics,
"security_group": security_group,
"login_mode":login_mode,
"login_passwd":login_passwd,
"login_keypair":login_keypair,
"need_newsid": need_newsid,
"need_userdata":need_userdata,
"userdata_type":userdata_type,
"userdata_value":userdata_value,
"userdata_path":userdata_path,
"userdata_file":userdata_file,
"volumes": volumes,
"graphics_protocol": graphics_protocol,
"graphics_passwd": graphics_passwd,
"boot_dev": boot_dev,
"instance_class": instance_class,
"usb": usb,
"hypervisor": hypervisor,
"repl": repl,
"gpu": gpu,
"gpu_class": gpu_class,
"ivshmem": ivshmem,
"features": features,
"nic_mqueue": nic_mqueue,
"stop_on_error": stop_on_error,
"sriov_nic_type": sriov_nic_type,
"ib_sriov_type": ib_sriov_type,
"private_pkey": private_pkey,
"os_disk_encryption": os_disk_encryption,
"cipher_alg": cipher_alg,
"cmk_id": cmk_id,
"enable_webssh": enable_webssh,
# 此处省略100个
}
resp = self.send_http(http_data)
return resp
这海量的参数对调用方和维护者来说都是巨大的负担,实际必传的参数可能只有几个,但是在定义接口时,我们又希望定义完整和接口文档保持一致,包括参数该传什么类型,哪些必传,哪些非必传,默认值是什么。
从django和fastapi中找到一些灵感,那就是参数模型。 针对这种有海量参数的接口,可以将参数定义单独抽象一层,由模型来管理,编写者通过模型来维护参数即可。
具体用法:
推荐在对应ao
(接口定义)目录下,定义一个model.py
模块
导入aomaker.dataclass
,对对应的参数类进行装饰即可,然后用这个类的类属性来管理接口的所有参数。
如: model.py
from aomaker import aomaker
from apis.iaas.instance.constants import LoginModeEnum
__all__ = ["RunInstanceModel", "DescribeImagesModel"]
@aomaker.dataclass
class RunInstanceModel:
action: str = "RunInstances"
image_id: str
instance_type: str
cpu: int = None
memory: int = None
instance_name: str = None
os_disk_size: int = None # 单位GB
count: int = 1
login_mode: LoginModeEnum = LoginModeEnum.passwd.value
login_passwd: str = "Zhu@88jie123"
login_keypair: str = None
vxnets: str = None # 是否加入私有网络
security_group: str = None # vxnets包含基础网络才需要
volumes: str = None
hostname: str = None
need_newsid: int = None # 默认0,只对windows类主机有效
instance_class: int = None # 主机性能类型
cpu_model: str = None # CPU指令集,有效值: Westmere, SandyBridge, IvyBridge, Haswell, Broadwell
cpu_topology: str = None
gpu: int = None
gpu_class: int = None
nic_mqueue: int = None # 网卡多队列,0关闭(默认),1开启
@aomaker.dataclass
class DescribeImagesModel:
action = "DescribeImages"
provider: str = "system"
status: list = ["pending", "available", "suspended"]
offset: int = None
limit: int = None
sort_key: str = "order_name"
os_family: list = ["centos"]
search_word: str = ""
模型中会定义每个接口的所有参数及其类型,当设置为None时,代表该参数非必填,在发送请求时,请求构造器会自动过滤掉这些非必填且没有传值的参数。
那么在一个接口定义中如何使用呢?
from apis.iaas.instance import model
def run_instances(self, **kw_params):
"""创建主机"""
params_model = model.RunInstanceModel(**kw_params)
resp = self.send_http(params_model)
return resp
这个接口的所有入参,通过不定长关键字参数传入,
如果传入了参数,那么会替换模型中的默认值;
如果传入了模型中没有的参数,会直接报错;
如果什么都不传,则默认取参数模型中的值,不过,要注意模型中是否有必传参数。
这样,整个接口定义会非常清爽,而复杂的接口参数管理可以通过接口文档自动生成我们规定的模型格式,单独管理,调用方也能非常清晰的知道每个参数的含义、是否必传或默认值等详细信息。