• 云服务多语言 SDK


    原文地址

    文中出现的代码模板地址

    简介

    云服务一般提供了一个文档来描述提供的 OpenAPI,然会每个用户根据文档自己实现签名、调用等功能的封装,这部分会出现各种各样的问题,因此用户更希望平台直接提供一个 SDK。

    darabonba

    darabonba 是阿里云开源的用于 OpenAPI 的 DSL 语言,编写 darabonba 就可以生成多语言的 SDK 等等。本文不介绍入门(README 比较全)。

    编写 SDK

    首先是 Config 类,一般包括 appKey 和 secretKey,并定义模块变量,定义如下:

    // 引入的依赖见最后的推荐依赖
    import Util;
    import SignatureUtil;
    import OpenApiUtil;
    
    type @host = string
    type @appKey = string
    type @secretKey = string
    
    model Config {
      appKey: string(description = "appKey"),
      secretKey: string(description = "secretKey")
    }
    

    接着是 Client 类的初始化方法里初始化:

    init(config: Config) {
      @host = "www.aliyun.com";
      @appKey = config.appKey;
      @secretKey = config.secretKey;
    }
    

    接着定义请求方法:

    // 请求的通用参数封装为一个 model
    model Request {
      // GET/POST
      method: string,
      // 请求路径
      pathname: string,
      // 拼接参数字符串,作为签名的一部分
      param: string,
      // 参数是否是 Json 格式
      isJson?: boolean
    }
    
    // http 请求,返回一个任意类型的结果
    api _requestRAny(request: Request, body: any): any {
      __request.pathname = request.pathname;
      __request.method = request.method;
      // 获取时间戳
      var date: string = OpenApiUtil.getTimestamp();
      // 计算签名
      __request.headers = _header(@appId, date, _sign(request.param, date));
      if (request.isJson) {
        __request.headers.accept = 'application/json';
        __request.body = Util.toJSONString(body);
      } else {
        __request.query = Util.stringifyMapValue(Util.assertAsMap(body));
      }
    } returns {
      // 处理 http response
      return _handle(__response);
    } runtime {
      // 超时时间 10 s,你也可以配成模块参数
      timeout = 10000
    }
    
    // 调用 api 的函数必须用 async 关键字修饰
    async function _requestRObj(request: Request, body: any): object {
      // object 等价 map[string]any 等价 $Model,它们之间可以直接转换
      return Util.assertAsMap(_requestRAny(request, body));
    }
    
    // 处理 http response,返回任意类型的结果
    function _handle(response: $Response): any {
      // 读取响应的数据,通常是一个包含 code、status 和 data 的 Json 串
      var result = Util.assertAsMap(Util.readAsJSON(response.body));
      if (!Util.equalNumber(response.statusCode, 200) || !Util.assertAsBoolean(result.ok)) {
          // 抛异常,通过 throw 关键字仅能抛出一种异常
          throw {
              message = `httpCode: ${response.statusCode}, serverCode: ${result.code}, reason: ${result.message}`,
              code = `${result.code}`
          };
      }
      return result.data;
    }
    
    // 构建请求头
    function _header(appKey: string, signedAt: string, sign: string): map[string]string {
      var headers: map[string]string = {
        host = @endpoint_host,
        app_key = appKey,
        signed_at = signedAt,
        sign = sign
      };
      return headers;
    }
    
    // 签名
    function _sign(param: string, date: string): string {
      // 模式字符串拼接参数,计算 md5 摘要做签名
      var sign: string = OpenApiUtil.hexEncode(SignatureUtil.MD5Sign(`app_key${@appKey}secret_key${@secretKey}signed_at${date}${param}));
      return sign;
    }
    

    比如一个接入用户接口

    model UserRequest {
      userId: string(description = "userId", name = "userId", required = true),
      nickName?: string(description = "nickName", name = "nickName")
    }
    
    async function createUser(userRequest: UserRequest): void {
      // 校验参数,UserRequest 的 nickName 使用了 ?:,? 表示不会进行校验,如果不调用这个方法那就没有区别
      Util.validateModel(userRequest);
      // 构建通用参数
      var request = new Request {
        method = @get,
        pathname = "/user/createUser",
        // 模式字符串拼接参数做签名
        param = `nickName${userRequest.nickName}userId${userRequest.userId}`
      };
      // 发起 http 请求,如果返回值是基本类型比如数字,那么可以这样:
      _requestRAny(request, accessThirdPartyUserRequest);
    }
    

    其他

    1. Model 和 Object 和 map[string]any

    它们三个是等价的,可以直接转换,不用转换,其他的都需要调用工具类转换,这点很重要

    1. 接口参数很少,不想定义 Model?

    直接使用 map[string]any。

    _requestRAny(request, {
      // 和前面 _header 函数一样构建 map 一样
      thirdPartyUserId = thirdPartyUserId
    })
    
    1. 接口参数包含数组,签名怎么计算?

    调用 OpenApiUtil.arrayToStringWithSpecifiedStyle() 方法转为为字符串

    // 风格自己选,和服务端保持一直就可以
    var arrayString: string = OpenApiUtil.arrayToStringWithSpecifiedStyle(null, null, null);
    
    1. 返回值类型怎么转换?

    _requestRAny 函数返回的是任意类型。

    • 基本类型使用 Util.assertAsXXX() 转换
    • 数组类型需要定义一个 model,在 model 里定义一个数组,然后用 map 设置数组再转换为 model
    model Response {
      items: [ string ](description = "items")
    }
    
    // 返回 Response 类型,函数其余部分不展示了
    return {
      items = _requestRAny(request, modelRequest)
    };
    

    模板

    darabonba-sdk-template 提供了一个快速开发的模板,只需要编写请求路径、入参即可生成多语言的 SDK。另外还提供了 Code Sample,可以为编写的 OpenApi 生成多语言的测试样例

    推荐依赖

  • 相关阅读:
    C#MessageBox的使用
    VBA技术资料MF85:将工作簿批量另存为PDF文件
    vue 项目挂载 刷新后报404
    【Web_接口测试_Python3_日期时间库】Arrow获取过去/当前未来时间日期、格式化时间日期、转换时间戳、获取不同时区时间日期等
    FIFO(同步与异步)
    神舟电脑怎么清理缓存文件?介绍几种简单有效方法
    Vue3中全局配置 axios 的两种方式
    MTU简介
    混迹职场10多年的数据开发老鸟,居然被一个职场新人上了一课
    07 _ 链表(下):如何轻松写出正确的链表代码?
  • 原文地址:https://www.cnblogs.com/hligy/p/16714954.html