交互协定 status_code.py

理论背景

实际开发中数字来表示不同的状态,状态较少时直接配置常量定义即可,如

# 使用时直接使用变量名,含义更清晰
PREPARE = 0
GET_TASK = 1
DO_TASK = 2
CLEAN_UP = 3

HTTP协议客户端与服务端交互的协议,双方定义的状态码较多,需采用一种更加灵活的方式

源码分析

# 定义原始数据结构
_codes = {
    200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'),
    301: ('moved_permanently', 'moved', '\\o-'),
    302: ('found',),
    404: ('not_found', '-o-'),
    500: ('internal_server_error', 'server_error', '/o\\', '✗'),
}

# codes全局变量,LookupDick对象,codes.okay或codes['okay'] 都行
codes = LookupDict(name='status_codes')

# 因requests/__init__.py中from .status_codes import codes,所以会执行该模块代码

def _init():
    for code, titles in _codes.items():
        for title in titles:
            setattr(codes, title, code)
            if not title.startswith(('\\', '/')):
                setattr(codes, title.upper(), code)
# 导入时已触发执行: 全局变量codes经过setattr把所有短语和状态码进行绑定
_init()
  • 全局项目可以使用codes.name来表示相应状态码

  • _codes作为原始数据源,自动加载设置成codes

项目实践

1. API通用返回格式

前后端分离开发离不开双发返回状态码的格式定义,随着业务的不断增长,状态码定义需可配置

from status_code import store as sc
from flask import jsonify
class APIResult(dict):
    def __init__(self, code, result=None, msg=None)
        self['code'] = code
        self['msg'] = msg or sc.get_error_msg(code)
        self['result'] = result if result is not None else {}
    
    def __call__(self, *arg, **kw):
        return self.jsonify()
    
    def jsonify(self):
        json_resp = jsonify(**self)
        json_resp.headers['Cache-Control'] = 'no-cache'
        return json_resp

# resp 就是flask 标准的json序列化后的返回对象
# 格式规定:code, result, msg  其中code与msg相对应
resp = APIResult(0, result={"data": [1, 2, 3], msg="成功"})()

如何实现可配置扩展的状态码映射结构体

  • 定义一个结构体存储 错误码: (错误代码名称,默认错误信息)

  • 生成的对象支持 store.E_SUCC

import types

DEFAULT_DICT = {
    0: ('E_SUCC', '成功'),
    1: ('E_PARAM', '参数错误'),
    2: ('E_INTER', '程序内部错误'),
    3: ('E_EXTERNAL', '外部接口错误'),
    4: ('E_TIMEOUT', '第三方接口超时'),
    5: ('E_RESRC', '接口不存在'),
    6: ('E_AUTH', '鉴权失败'),
    7: ('E_FORBIDDEN', '访问被禁止'),
    8: ('E_RESOURCE_NOT_FIND', '资源不存在或已删除')
}

class StatusCodeStore(object):
    DEFAULT_STORE = None
    def __init__(self, codes=None):
        self.codes = codes if type(codes) is dict else {}
        self.refresh()
    
    def refresh(self):
        self.reverse = {}
        set_into_modeluls(self.reverse, from_store=self)
    
    def get_error_msg(self, code):
    if isinstance(code, str) and code.isdigit():
        code = int(code)
    _, msg = self.codes.get(code, (None, None)
    return msg or '未知错误'
    
    def __getattr__(self, name):
        code = self.reverse[name]
        return code

def set_into_modules(target, from_store=None):
    from_store = StatusCodeStore.DEFAULT_STORE if from_store is None else from_store
    
    if isinstance(target, dict):
        target_dict = target
    elif isinstance(target, types.ModuleType):
        target_dict = target.__dict__
    for (code, (name, msg)) in from_store.codes.items():
        target_dict[name] = code

store = StatusCodeStore.DEFAULT_STORE = StatusCodeStore(DEFAULT_DICT)
  • 根据项目需要,可以自定义status_code.json文件,或者单独项目以供多个项目使用

2. 函数之间标识符

# 定义方
def func():
    flag = True
    if flag:
        print("Flag is OK.")
        return True, None
    return False, "msg"
    
# 调用方
code, msg = func()
# 根据code码来判断后续执行逻辑

3. 多人之间协议

from collections import namedtuple

# 开户基本信息
OpenProtocol = namedtuple(
    "OpenProtocol",
    "identity_card,"
    "identity_type,"
    "bank_number,"
    "data"
)

# 一方做本地的业务操作,一方做第三发连接操作,只要都满足OpenProtocol协议字段即可

Last updated