接口规范

基础接口规范前言

该接口规范旨在达到以下目的

  • 接口请求参数规范
  • 接口响应参数规范
  • 请求参数校验规范
  • 通用异常处理规范

适用场景

  • 模块间服务接口调用
  • 前端 REST 服务接口调用

接口返回值规范

所有对外服务接口必须使用 Result Bean 封装响应的结果

响应基类BaseResult

public class BaseResult<T> implements IResult {
private Boolean isSuccess;// 本次调用是否成功
private IStatusCode code;// 状态编码
private String message = "";//
private String stackTrace = "";
...
}

通用响应类

public class ResultMsg<T> extends BaseResult<T>{
private T data = null; // 返回数据
/**成功,有结果数据 * @param data */
public ResultMsg(T result){
this.setIsSuccess(true);
this.setData(result);
}
...
}

分页类型响应类

public class PageResult<T> extends BaseResult<T>{
private Integer pageSize = 0;//分页大小
private Integer page = 1;//当前页
private Integer total = 0;//总条数
private List rows = null;//分页列表数据
...

响应码规范

响应需要有对应的响应码,对于业务异常、系统可预知异常需要定义好异常码,并使用 包装异常 ,不同模块需要定义自身的异常枚举,并实现接口IStatusCode

/**
* @说明 系统状态码定义抽象接口<br>
* 子模块或者系统需要定义自己的系统状态码<br>
* @StatusCode 为基础模块定义的基本码值,1~500 目前已经被占用
* @author jeef
*
*/
public interface IStatusCode {
/**
* 状态码
* @return
*/
public String getCode() ;
/**
* 异常信息
* @return
*/
public String getMsg() ;

/**
* 系统编码
* @return
*/
public String getSystem() ;

}

基础响应码实现

/**
* @说明 基础系统状态码定义
*/
public enum StatusCode implements IStatusCode{

SUCCESS("200","成功"),
SYSTEM_ERROR("500","系统异常"),
TIMEOUT("401","访问超时"),
NO_ACCESS("403","访问受限"),
PARAM_ILLEGAL("100","参数校验不通过");

private String code;
private String msg;
private String system;

StatusCode(String code,String msg){
this.setCode(code);
this.setMsg(msg);
this.setSystem("BASE");
}

public String getCode() {
return code;
}
....
}

异常定义规范

AgileBPM 异常定义均在 com.dstz.base.api.exception 包,各个业务模块不需要定义特殊的异常类,均通过异常码来标识异常!
异常分三种

  • 业务消息 BusinessMessage
  • 业务异常 BusinessException
  • 系统异常BusinessError

业务消息异常

通常为直接通知用户 业务消息,通常用于业务代码反馈,非系统异常!
如,保存用户时,后端校验 账号已存在 ,可以直接抛出业务异常 BusinessMessage ,并返回定义好的状态码

普通异常

业务逻辑异常,常常为可预料异常情况的抛出或者非法操作的信息提示。比如: 流程表单丢失!

系统异常

BusinessError 系统异常,常常用于强制捕获的异常的包装,是相对紧急程度高或者比较严重异常

所有异常均会中断事务,业务消息不会记录异常堆栈、日志 ,会直接提示到前端用户错误信息
其他则会详细记录请求信息、堆栈信息,并返回异常码,不会返回具体异常信息,前端应给予“系统服务异常”的提示

请求异常捕获与校验

当我们需要对一个请求接口的异常情况进行统一处理的时候,可以使用该注解
当方法体出现异常时,会包装标准的异常 resultMsg 进行反馈给调用者,并记录日志

比如前端请求、RPC 调用者等一些场景,省去了我们异常捕获的动作,也规范对于前端的响应 Rest 当然也可以对Controller层全局异常处理,但目前我们没这么做。

eg:

@CatchErr("保存用户失败")
@ParamValidate
public ResultMsg<String> saveUser(@RequestBody User user){
if (userManager.isUserExist(user)){
throw new BusinessMessage("用户在系统中已存在!");
}
// do save
return getSuccessResult(user.getId(), "保存成功");
}

注解CatchErr介绍

/**
* @描述 <p>如果该注解的方法出现异常,则会反馈标准的异常结果【ResultMsg.java】给前端或者服务调用方</p>
* @提示 <p>使用该注解需要注意事物问题更多信息请查看ErrAspect.java</p>
*
* @param write2response 是否写入到response 用于http请求中,方法会从入参中获取response,然后写入resultMsg<br>
* @param needStackTrace 是否需要反馈异常堆栈信息,默认不反馈堆栈异常,但是堆栈异常会进行日志记录
* @param value 方法体异常描述
* @create 2017-11-19 20:31:00
* @author jeff
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CatchErr {
String value() default "";
boolean write2response() default false ;
boolean needStackTrace() default false;
}

请求参数校验

请求参数校验使用的 javax validate3.0规范 hibernate-validator 的一个实现

@CatchErr
@ParamValidate
public PageResult<String> getRolesByUser(User user){

List<String> roles = Arrays.asList("1,2,3,4,5,6,7,8,9".split(","));
PageList<String> pageList = new PageList<String>(2, 10, 100, roles);

PageResult<String> msg = new PageResult<String>(pageList);

return msg;
}
public class User {
@NotBlank(message = "姓名不能为空")
private String name = "";
@NotBlank(message = "账号不能为空")
private String account = "";
...
}

ParamValidate 会对user对象进行校验

校验不通过则会反馈标准的resultMsg给调用者

引入CatchErr、@ParamValidate 注意事项

需注意必须在 springMVC的配置页配置 注解拦截器
否则将无法在controller层拦截处理异常

<aop:aspectj-autoproxy proxy-target-class="true"/>

前端统一处理

前端一些配套js

对jQuery进行扩展增加一些公共的 result 解析方法,用来提示系统错误,如 util.js getResultData 方法

/**
* @说明 获取请求响应的数据
* @失败 默认toast 提示错误信息
* @成功 不展示成功信息自己处理
*/
getResultData : function(defer,fn){
defer.then(function(result){
if(!result.isok){
toast.error(result.message);
console.error(result.stackTrace);
return;
}
//返回结果
fn(result.data);

},function(status){
alert("加载失败!"+status);
}
);
},

前端如此使用

   var defer=baseService.get(dataUrl);
$.getResultData(defer,function(data){
//这里直接使用data
});