接口规范¶
前后端分离,根据产品中心实践过程中经验累积,整理出一套后台接口规范,便于对接时规范,新员工快速掌握了解。
修订记录¶
版本号 | 制定团队 | 更新日期 | 发布对象 | 备注 |
---|---|---|---|---|
0.0.1 | 产品中心 | 2019/12/23 | 产品中心 | 初步整理 |
0.0.2 | 产品中心 | 2020/⅛ | 产品中心 | 详情整理 |
0.0.3 | 产品中心 | 2020/⅔ | 产品中心 | 整合调整 |
0.0.4 | 产品中心 | 2020/⅖ | 产品中心 | 审核调整 |
管理平台¶
所有接口文档通过 YApi 平台进行管理和维护,详见YApi 使用文档,接口采取先设计后开发的原则,后端接口开发完成前,前端使用Mock
功能进行开发。
数据请求规范¶
请求参数¶
header参数部分¶
请求头部放入鉴权的相关参数,比如用户的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 格式 |
接口设计规范¶
设计原则¶
- 安全可靠性原则:系统应提供良好安全策略,接口应提供安全可靠的鉴权、认证机制;
- 可扩展性原则:支持新业务的扩展以及接口性能的提高;
- 兼容性原则:接口的变动要有兼容说明、对于不兼容的接口需要有升级方案;
- 统一性原则:保证系统的接口形式、使用的协议等标准统一。
命名规范¶
-
【强制】请求路径多个字段使用 '-' 链接.
-
正例:@RequestMapping(value="/user/batch-delete",method = RequestMethod.POST)
-
反例:@RequestMapping(value="/user/batchDelete")
-
【强制】请求@RequestMapping的method 必须指定请求方式
-
【强制】请求参数 遵循驼峰命名法
-
正例:@Param(name = "businessName", type = String.class,required = true)
-
反例:@Param(name = "business_name", type = String.class,required = true)
@Param(name = "business-name", type = String.class,required = true)
参数规范¶
-
【强制】请求参数类型定义类型与前端定义一致 (businessName 前端传的是String类型,type=String.class),
-
正例:@Param(name = "businessName", type = String.class,required = true)
-
反例:@Param(name = "businessname", type = Integer.class,required = true)
-
【强制】获取参数类型说明
-
接收指定参数类型
- 获取非必传参数使用 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")) ;
}
-
【强制】上传接口不能重写,使用human、system 已有的接口.
-
【强制】参数返回单个值使用ImmutableMap.of("result",boolean)封装.
-
【强制】接口调用必须有返回值.
-
正例:public member disable(RequestContext requestContext) { return member; }
-
反例:public void disable(RequestContext requestContext) { XXXXXX }
-
【强制】JSON数据传输转换,返回具体的属性值.
-
正例:@JSON("id,name,createTime,businessId,businessName")
-
反例:@JSON("*"),容易出现传输数据异常,导致返回前端 数据为null,需要重启服务
-
【强制】接口异常使用throw new UnprocessableException(ErrorCode.ApprovalRelationshipBusiness),在ErrorCode里面定义具体业务错误.
-
【强制】根据业务判断是否需要验证登陆添加@Permitted 注解或使用Subject
subject -
正例:
@Permitted
public Member findById(Subject<Member> subject){return member;}
-
用户信息、企业id通过subject.getCurrentUser()对象获取,原理请看MemberSecurityManager 配置
-
【建议】审计使用说明 -> 审计使用规范
-
正例
@Audit(module = "系统管理", subModule = "审批管理", action = Audit.Action.UPDATE, fisrtAction = "编辑", desc = "编辑课程发布审批模板{0}", params = {"name"},businessType="type",businessValue="1")
-
【建议】越权使用说明,在新增、编辑、删除、启用、禁用、等业务时需要判断接口的权限,需要检测,详细说明请看->越权.
-
【建议】接口频次控制,根据业务要求是否使用频次 ->频次使用规范.
-
【建议】请求参数 加密,若因为安全或业务,需要参数加密,可使用@Param(name = "passWord", required = true, security = true),详细了解加密原理->9.0前端安全传输方案
-
【建议】若接口不需要请求参数 ,后台做了拦截处理,避免使用的时候报错,建议使用@Params标识.
-
【阿里建议】在使用平台资源,譬如短信、邮件、电话、必须实现正确的防重放限制,
1.如数量限制、验证码校验,避免被滥刷导致资损。
说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其
它用户,并造成短信平台资源浪费
-
【阿里建议】用户请求传入的任何参数必须做有效性验证。
- 忽略参数校验可能导致:
·page size 过大导致内存溢出
· 恶意 order by 导致数据库慢查询
· SQL 注入
后门接口修复规范¶
-
【强制】写清该接口修复什么问题,Wiki地址统一收纳后台修复接口 -> 后台修复接口维护.
-
【强制】后台修复接口必须可重复执行
-
【建议】后门接口时效性,判断是否需要下个版本进行优化处理.
-
【建议】后门接口统一管理->后续可通过注解、开关控制、避免隐患
终端调用规范¶
- 【建议】业务接口按终端区分包名,更清晰显示业务,后续扩展灵活.
- 正例:
- app -> com.zxy.product.system.web.controller.app.*
- 学员端 -> com.zxy.product.system.web.controller.student.*
- 管理端 -> com.zxy.product.system.web.controller.admin.*
- 通用 - > com.zxy.product.system.web.controller.base.*
RPC接口规范¶
-
【强制】插入方法前缀insert, 更新方法前缀update, 删除方法前缀delete, 根据id查找前缀get, 其它查找前find
-
如果方法名后缀与接口名前缀相同, 则省略:
public class RoleService { Role insertRole(); // 这里的insertRole应该省略insert Role insert(); //改成这种 }
-
如果同类型的方法有多个, 则根据参数添加后缀区分.
- 如:findById(String id)、findByName(String name)
-
【强制】所有接口方法都要打上
@Transactional
标记, 所有的查询方法都要加上readonly=true.
-
【强制】所有的service根据业务进行分包,定义实际业务,
1.例如 project.api.module, project.api.support.module
-
【强制】server接口不允许跨大模块调用,如需使用,在web工程调用.
-
如:courser的service模块内,不能直接调用system的service
-
【强制】参数没有超过10个,不能传实体.
-
正例: public User insert(String username, Optional
realName) -
反例: public User insert(User user) // 不允许传实体
-
【强制】接口方法不允许返回类型为
void.
-
【强制】分页接口使用PagedResult<?> findForMemberId(int page, int pageSize) 格式规范.
-
【强制】接口调用超过3s,必须优化逻辑
-
【强制】接口里面不用写 public.
-
【强制】接口返回值不能使用ImmutableMap.of("result",boolean)封装.
- 说明:ImmutableMap.of()参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属性值都会丢失.
-
【强制】在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、 删除均会产生 ConcurrentModificationException 异常。
-
【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常, 即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. 说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。
-
【建议】大数据量调用返回,建议调用方进行分批处理,避免rpc远程调用超时.
-
【建议】方法不使用,不知是否被其他模块调用,使用@Deprecated标识过期
/** * 查询xxxxx * 原因:此接口过期,后续不在维护,XXXXX * 若需要此数据,请调用 findById(String orgId); * @param organizationId * @return */ @Deprecated @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) List<Setting> find(String organizationId);
扩展思考¶
安全¶
- 接口不能传递任何明文密码,客户端不允许缓存明文密码
- 用户敏感信息(手机号、身份证号码等)传递需要做加密或脱敏处理
- 对于数据操作接口需要对用户进行鉴权,防止越权操作(详见系统越权解决方案)
- 对于枚举类型参数,需要白名单过滤,防止非法传入安全漏洞,将非法调用隔离开来
性能¶
- 复杂业务流程是否需要异步处理
- 根据实际业务场景使用缓存
兼容¶
接口的变化一般会有几种:
- 数据的变化,比如增加了旧版本不支持的数据类型(兼容:新增接口版本,接口增量更新)
- 参数的变化,比如新增了参数(兼容:新增接口版本,接口增量更新)
- 接口的废弃,不再使用该接口了(不兼容:原接口指定版本废弃,后端逻辑处理;原接口所有版本废弃,如果是业务流程修改,则停用原接口,并新开接口)
- 如果整个接口系统的根基都发生变动的话,整个API都进行了升级,就无法兼容,只能进行版本强制升级了
其他思考¶
- 接口功能定义是否明确,是否有功能重复的地方
- 接口的数据量是多少,是否需要使用分批传输等机制