api设计最佳实践

api设计最佳实践

  • 参数设计
    • 固定参数采用基本类型
    • 参数过多(8个)建议采用对象参数
    • 接口预留扩展对象,便于后期处理个性化需求
    • 参数需要考虑某些涉及到资金的字段是否自增的,暴露之后是否容易猜到
  • 返回值设计
    • 提供通用的接口调用成功失败的方法
    • 实现Serializable并重写id
    • 规避使用枚举
    • 结构体
      {
        "code": 200,
        "model": "",
        "msg": ""
      }
      复制代码
    • 对象设计要简练
      • 通用逻辑服务端处理好,前端只做展示,例如:标签展示,服务端抽象好标签文案和类型即可,前端根据类型在 做定制化的样式展示,标签可以任意扩展
    • 相同作用域收敛
      • 中台结构往往需要结合各个基础服务获取到的信息封装一个fat对象,那么此时我们需要对对象进行DDD模型抽象,划分不同的作用域,例如商品的资金信息,对于前端来说只需要展示价格的原价和现价,那么我们需要将这两个价格的逻辑收敛到这两个字段中,无需前端再做其他业务处理
    • 逻辑控制要收敛
      • 对于同一个字段,在不同业务场景下代表不同的值针对该字段增加处理类,逻辑收敛
    • 根据业务逻辑分层
      • 根据返回对象的业务,定好对象的父子关系,通用属性归属到父类,个性属性归属子类对象
  • 异常处理
    • 枚举定义错误码,支持可配置的错误文案
    • 注解环绕的方式处理异常,逻辑收敛
  • 功能特性
    • 对于会产生数据变更的服务,做好防止重复调用处理
    • 最短路径原则
    • 懒加载方式使用的时候再加载
    • 对于中间数据需要在多层次的方法调用中传递构建Context,放置到ThreadLocal中实现线程中随处访问,但是需要注意内存泄漏问题(1,线程池 2.使用之后主动释放)
    • 通用的判定逻辑收敛到静态类中,便于后期判定逻辑修改,漏改
    • 对于下游接口调用,本着先怀疑的态度,注意判定空
  • 非功能特性
    • 性能
      • 限流降级,不返回数据不会对用户产生影响的接口可以采用熔断的策略,如果返回数据抽象核心和非核心,在流量过大的时候可以降级非核心业务
        • sentinal
        • 信号量
        • hytrixs
      • 底层并行调用
        • 并发调用
        • RxJava
      • 多场景下的综合服务,底层力度控制
        • 通过设计控制位,针对不同的业务场景分配不同控制位,减少不必要的调用
    • 安全性
      • 登录态,用户认证
      • 敏感数据脱敏,用户的个人隐私数据脱敏 收集号码中间4位占位展示
    • 扩展性
      • 接口升级
        • 思考接口升级的时候兼容问题,是否涉及到外部调用大量修改,
      • 个性化需求
    • 健壮性
      • 下游服务调用容错处理

        • 自定义注解环绕
           @Target({ElementType.METHOD})
           @Retention(RetentionPolicy.RUNTIME)
           @Documented
           public @interface ResultWrapper {
               DefaultResultEnum defaultObj() default DefaultResultEnum.OBJECT;
           }
        复制代码
        • 定义返回类型避免空指针
          LIST(1, "list", Collections.EMPTY_LIST),
          MAP(2, "map", Collections.EMPTY_MAP),
          SET(3, "set", Collections.EMPTY_SET),
          OBJECT(4, "object", null),
          STRING(5, "string", ""),
          BOOLEAN(6, "boolean", Boolean.FALSE),
          复制代码
      • 统一下游服务返回对象

        Result<T> implements Serializable {
              
                  /**
                   * 返回码
                   */
                  private int code = 200;
              
                  /**
                   * 报错信息
                   */
                  private String msg;
              
                  /**
                   * 返回model
                   */
                  private T model;
        
                public boolean isSuccess(){
                    return 200==code;
                  }
        
        复制代码
      • 整体接口容错处理

        • 自定义注解环绕
           @Around(value = "@annotation(serviceWrapper)", argNames = "joinPoint,serviceWrapper")
                          public Object doAround(ProceedingJoinPoint joinPoint, ServiceWrapper serviceWrapper) throws Throwable {
                              final long begin = System.currentTimeMillis();
                              Object object = serviceWrapper.isReturnBooleanType() ? false : null;
                              try {
                                  object = joinPoint.proceed();
                                  return object;
                              }  catch (Exception e) {
                                  LogControl.printLogWarn("exception", Throwables.getStackTraceAsString(e),JSON.toJSONString(joinPoint.getArgs()));
                                  DubboExtProperty.setErrorCode(GwReturnCode.SYSTEM_ERROR);
                              }
                              return object;
                          }
          复制代码
        • 问题查询的便捷性
          • 关键节点日志打印,代码设计的时候就需要预判可能出现的问题,和查询该问题需要的关键信息
            • 重写toString方法,重点打印过程数据,如果接口会暴露出来的数据,建议只保留关键信息如:主键id
        • 抛砖引玉,这是我在设计一个接口的时候考虑到的一些因素,大家如果有好的建议的欢迎留言补充
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享