跳转至

接口规范

前后端分离,根据产品中心实践过程中经验累积,整理出一套后台接口规范,便于对接时规范,新员工快速掌握了解。

修订记录

版本号 制定团队 更新日期 发布对象 备注
0.0.1 产品中心 2019/12/23 产品中心 初步整理
0.0.2 产品中心 2020/⅛ 产品中心 详情整理
0.0.3 产品中心 2020/⅔ 产品中心 整合调整
0.0.4 产品中心 2020/⅖ 产品中心 审核调整

管理平台

所有接口文档通过 YApi 平台进行管理和维护,详见YApi 使用文档,接口采取先设计后开发的原则,后端接口开发完成前,前端使用Mock 功能进行开发。

数据请求规范

请求参数

请求头部放入鉴权的相关参数,比如用户的token和签名、设备id、APP的标识、userAgent自定义等。

参数名 类型 说明 示例
Authorization String 鉴权 token Bearer__7c14c78aa51fcfa6f85b57736aa85f98
uri String 菜单路径 exam/exam

接口入参传递

  • 一维参数:按照GET/POST/PUT按照普通的form-data和urlencode方式即可
  • 多维参数:按照POST/PUT方式,以json的形式传递

接口返回数据

错误信息的返回:主接口中不直接对错误信息进行定义。只要接口能够正常请求(HTTP Status Code 200)并获取到数据,就认为是正确的调用,而错误信息的定义则是用另一种数据格式来表示。

HTTP Status Code

状态码 说明
200 请求成功
422 出现错误(详见下文错误信息格式)
401 未登录
403 无权限请求

错误信息数据格式

key 类型 说明 示例
errorCode int 错误编码(一般为 6 位数),具体接口文档中需要对编码定义及业务释义作出详细说明 { "errorCode": 900000, "extend": {} }
extend object 扩展数据,json 格式

接口设计规范

设计原则

  • 安全可靠性原则:系统应提供良好安全策略,接口应提供安全可靠的鉴权、认证机制;
  • 可扩展性原则:支持新业务的扩展以及接口性能的提高;
  • 兼容性原则:接口的变动要有兼容说明、对于不兼容的接口需要有升级方案;
  • 统一性原则:保证系统的接口形式、使用的协议等标准统一。

命名规范

  1. 【强制】请求路径多个字段使用 '-' 链接.

  2. 正例:@RequestMapping(value="/user/batch-delete",method = RequestMethod.POST)

  3. 反例:@RequestMapping(value="/user/batchDelete")

  4. 【强制】请求@RequestMapping的method 必须指定请求方式

  5. 【强制】请求参数 遵循驼峰命名法

  6. 正例:@Param(name = "businessName", type = String.class,required = true)

  7. 反例:@Param(name = "business_name", type = String.class,required = true)

    ​ @Param(name = "business-name", type = String.class,required = true)

参数规范

  1. 【强制】请求参数类型定义类型与前端定义一致 (businessName 前端传的是String类型,type=String.class),

  2. 正例:@Param(name = "businessName", type = String.class,required = true)

  3. 反例:@Param(name = "businessname", type = Integer.class,required = true)

  4. 【强制】获取参数类型说明

  5. 接收指定参数类型

  6. 获取非必传参数使用 getOptionalXXXXX();
@RequestMapping(value = "revoke",method = RequestMethod.GET)
@Param(name = "businessId", type = String.class, required = true)
@Param(name = "businessName", type = String.class)
@JSON("businessId")
public Map<String,String> revoke(RequestContext context, Subject<Member> subject){
     String businessId = context.getString("businessId");  //指定参数类型获取
     //获取非必传的参数、参数类型注意下getOptionalString、getOptionalInteger等
     Optional<String> businessName= context.getOptionalString("businessName"); 
  return ImmutableMap.of("businessId",context.getString("businessId")) ;
 }
  1. 【强制】上传接口不能重写,使用human、system 已有的接口.

  2. 【强制】参数返回单个值使用ImmutableMap.of("result",boolean)封装.

  3. 【强制】接口调用必须有返回值.

  4. 正例:public member disable(RequestContext requestContext) { return member; }

  5. 反例:public void disable(RequestContext requestContext) { XXXXXX }

  6. 【强制】JSON数据传输转换,返回具体的属性值.

  7. 正例:@JSON("id,name,createTime,businessId,businessName")

  8. 反例:@JSON("*"),容易出现传输数据异常,导致返回前端 数据为null,需要重启服务

  9. 【强制】接口异常使用throw new UnprocessableException(ErrorCode.ApprovalRelationshipBusiness),在ErrorCode里面定义具体业务错误.

  10. 【强制】根据业务判断是否需要验证登陆添加@Permitted 注解或使用Subject subject

  11. 正例:

   @Permitted
   public Member findById(Subject<Member> subject){return member;}
  1. 用户信息、企业id通过subject.getCurrentUser()对象获取,原理请看MemberSecurityManager 配置

  2. 【建议】审计使用说明 -> 审计使用规范

  3. 正例

    @Audit(module = "系统管理", subModule = "审批管理", action = Audit.Action.UPDATE, fisrtAction = "编辑", desc = "编辑课程发布审批模板{0}", params = {"name"},businessType="type",businessValue="1")
    
  4. 【建议】越权使用说明,在新增、编辑、删除、启用、禁用、等业务时需要判断接口的权限,需要检测,详细说明请看->越权.

  5. 【建议】接口频次控制,根据业务要求是否使用频次 ->频次使用规范.

  6. 【建议】请求参数 加密,若因为安全或业务,需要参数加密,可使用@Param(name = "passWord", required = true, security = true),详细了解加密原理->9.0前端安全传输方案

  7. 【建议】若接口不需要请求参数 ,后台做了拦截处理,避免使用的时候报错,建议使用@Params标识.

  8. 【阿里建议】在使用平台资源,譬如短信、邮件、电话、必须实现正确的防重放限制,

    1.如数量限制、验证码校验,避免被滥刷导致资损。

    说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其

    它用户,并造成短信平台资源浪费

  9. 【阿里建议】用户请求传入的任何参数必须做有效性验证。

    1. 忽略参数校验可能导致:

    ·page size 过大导致内存溢出

    · 恶意 order by 导致数据库慢查询

    · SQL 注入

后门接口修复规范

  1. 【强制】写清该接口修复什么问题,Wiki地址统一收纳后台修复接口 -> 后台修复接口维护.

  2. 【强制】后台修复接口必须可重复执行

  3. 【建议】后门接口时效性,判断是否需要下个版本进行优化处理.

  4. 【建议】后门接口统一管理->后续可通过注解、开关控制、避免隐患

终端调用规范

  1. 【建议】业务接口按终端区分包名,更清晰显示业务,后续扩展灵活.
  2. 正例:
    1. app -> com.zxy.product.system.web.controller.app.*
    2. 学员端 -> com.zxy.product.system.web.controller.student.*
    3. 管理端 -> com.zxy.product.system.web.controller.admin.*
    4. 通用 - > com.zxy.product.system.web.controller.base.*

RPC接口规范

  1. 【强制】插入方法前缀insert, 更新方法前缀update, 删除方法前缀delete, 根据id查找前缀get, 其它查找前find

  2. 如果方法名后缀与接口名前缀相同, 则省略:

    public class RoleService {
        Role insertRole();    // 这里的insertRole应该省略insert
    
        Role insert();  //改成这种
    }
    
  3. 如果同类型的方法有多个, 则根据参数添加后缀区分.

    1. 如:findById(String id)、findByName(String name)
  4. 【强制】所有接口方法都要打上@Transactional标记, 所有的查询方法都要加上readonly=true.

  5. 【强制】所有的service根据业务进行分包,定义实际业务,

1.例如 project.api.module, project.api.support.module

  1. 【强制】server接口不允许跨大模块调用,如需使用,在web工程调用.

  2. 如:courser的service模块内,不能直接调用system的service

  3. 【强制】参数没有超过10个,不能传实体.

  4. 正例: public User insert(String username, Optional realName)

  5. 反例: public User insert(User user) // 不允许传实体

  6. 【强制】接口方法不允许返回类型为void.

  7. 【强制】分页接口使用PagedResult<?> findForMemberId(int page, int pageSize) 格式规范.

  8. 【强制】接口调用超过3s,必须优化逻辑

  9. 【强制】接口里面不用写 public.

  10. 【强制】接口返回值不能使用ImmutableMap.of("result",boolean)封装.

    1. 说明:ImmutableMap.of()参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属性值都会丢失.
  11. 【强制】在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、 删除均会产生 ConcurrentModificationException 异常。

  12. 【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常, 即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. 说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。

  13. 【建议】大数据量调用返回,建议调用方进行分批处理,避免rpc远程调用超时.

  14. 【建议】方法不使用,不知是否被其他模块调用,使用@Deprecated标识过期

     /**
         * 查询xxxxx
         * 原因:此接口过期,后续不在维护,XXXXX
         * 若需要此数据,请调用 findById(String orgId);
         * @param organizationId
         * @return
         */
        @Deprecated
        @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
        List<Setting> find(String organizationId);
    

扩展思考

安全

  • 接口不能传递任何明文密码,客户端不允许缓存明文密码
  • 用户敏感信息(手机号、身份证号码等)传递需要做加密或脱敏处理
  • 对于数据操作接口需要对用户进行鉴权,防止越权操作(详见系统越权解决方案
  • 对于枚举类型参数,需要白名单过滤,防止非法传入安全漏洞,将非法调用隔离开来

性能

  • 复杂业务流程是否需要异步处理
  • 根据实际业务场景使用缓存

兼容

接口的变化一般会有几种:

  1. 数据的变化,比如增加了旧版本不支持的数据类型(兼容:新增接口版本,接口增量更新)
  2. 参数的变化,比如新增了参数(兼容:新增接口版本,接口增量更新)
  3. 接口的废弃,不再使用该接口了(不兼容:原接口指定版本废弃,后端逻辑处理;原接口所有版本废弃,如果是业务流程修改,则停用原接口,并新开接口)
  4. 如果整个接口系统的根基都发生变动的话,整个API都进行了升级,就无法兼容,只能进行版本强制升级了

其他思考

  • 接口功能定义是否明确,是否有功能重复的地方
  • 接口的数据量是多少,是否需要使用分批传输等机制