统一返回结果肯定说的是controller,我们先假定controller都返回一样的格式。这时候就会对这个统一返回的格式有个定义,一般习惯是枚举,返回的结果有code、message、data等。
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* 通用返回结果
*
* @param <T> 结果泛型
*/
@Getter
@Setter
public class CommonResult<T> implements Serializable {
public static Integer CODE_SUCCESS = 0;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 返回数据
*/
private T data;
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
*
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
*
* @param result 传入的 result 对象
* @param <T> 返回的泛型
* @return 新的 CommonResult 对象
*/
public static <T> CommonResult<T> error(CommonResult<?> result) {
return error(result.getCode(), result.getMessage());
}
public static <T> CommonResult<T> error(Integer code, String message) {
Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!");
CommonResult<T> result = new CommonResult<>();
result.code = code;
result.message = message;
return result;
}
public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<>();
result.code = CODE_SUCCESS;
result.data = data;
result.message = "";
return result;
}
@JsonIgnore
public boolean isSuccess() {
return CODE_SUCCESS.equals(code);
}
@JsonIgnore
public boolean isError() {
return !isSuccess();
}
@Override
public String toString() {
return "CommonResult{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
CommonResult定义了静态方法,success、error等方法,提供给controller做响应。
说明:@Getter、@Setter是依赖了lombok,提供的注解。
对于异常的处理,算是系统的核心部分,异常一是排查问题解决问题,二是对响应的友好处理。
如果是简单的测试,那么我们try..catch就可以解决。
但是对于系统来说,最好是全局处理异常,将异常统一返回。
@ControllerAdvice(basePackages = "com.springswagger.springswagger01.controller")
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理 ServiceException 异常
*/
/*@ResponseBody
@ExceptionHandler(value = ServiceException.class)
public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex) {
logger.debug("[serviceExceptionHandler]", ex);
// 包装 CommonResult 结果
return CommonResult.error(ex.getCode(), ex.getMessage());
}*/
/**
* 处理 MissingServletRequestParameterException 异常
*
* SpringMVC 参数不正确
*/
@ResponseBody
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) {
logger.debug("[missingServletRequestParameterExceptionHandler]", ex);
// 包装 CommonResult 结果
return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(),
ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage());
}
@ResponseBody
@ExceptionHandler(value = ConstraintViolationException.class)
public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {
logger.debug("[constraintViolationExceptionHandler]", ex);
// 拼接错误
StringBuilder detailMessage = new StringBuilder();
for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {
// 使用 ; 分隔多个错误
if (detailMessage.length() > 0) {
detailMessage.append(";");
}
// 拼接内容到其中
detailMessage.append(constraintViolation.getMessage());
}
// 包装 CommonResult 结果
return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
}
@ResponseBody
@ExceptionHandler(value = BindException.class)
public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) {
logger.debug("[bindExceptionHandler]", ex);
// 拼接错误
StringBuilder detailMessage = new StringBuilder();
for (ObjectError objectError : ex.getAllErrors()) {
// 使用 ; 分隔多个错误
if (detailMessage.length() > 0) {
detailMessage.append(";");
}
// 拼接内容到其中
detailMessage.append(objectError.getDefaultMessage());
}
// 包装 CommonResult 结果
return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
}
/**
* 处理其它 Exception 异常
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {
// 记录异常日志
logger.error("[exceptionHandler]", e);
// 返回 ERROR CommonResult
return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),
ServiceExceptionEnum.SYS_ERROR.getMessage());
}
}
对于业务参数的验证,也可以将响应信息,以统一的形式返回。
对于验证,也可以采用统一的验证,yes or no ?我们引入了hibernate-validation框架。
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.3.Final</version>
</dependency>
在引入依赖后,我模拟的是一个登录操作,请求参数是一个body体。所以事先定一个对象专门用于登录的类。
import javax.validation.constraints.NotEmpty;
@ApiModel(value = "LoginDto", description = "用于登录验证Dto")
@Data
public class LoginDto {
@ApiModelProperty(value = "用户名")
@NotEmpty(message = "用户名不能为空")
@Length(min = 4, message = "用户名不能太短或太长")
public String userName;
@ApiModelProperty(value = "密码")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, message = "密码不能太短或太长")
public String password;
}
@NotEmpty、@Length都是hibernate validation框架中自带的验证,分别用于验证空和长度。这个时候的验证还是没起作用,需要在controller和具体的方法中增加对应的注解。
controller类上增加@Validated注解, 同时方法参数增加@Valid注解。
扩展:
对于参数的验证,
比如对于手机号、邮箱的验证,hibernate validation 并未提供,但是支持自定义,后续。。。。
比如添加时id可以为空,而修改时id不可以为空,这种要么定义两个对象分别验证,要么可以分组,比如一个规则用于添加一个规则用于修改,后续深入。。。。。