Springboot-优雅装备接口呼应和一致反常处理
一、前语
越来越多的项目开端根据前后端分离的形式进行开发,这对后端接口的报文格局便有了一定的要求。一般,咱们会采用JSON格局作为前后端交流数据格局,然后减少沟通本钱等。
@PostMapping("/user/add")
public ResponseInfo addUser(@RequestBody @Validated User user) {
userService.add(user);
return ResponseInfo.success();
}
咱们看看这个代码,有什么问题。 咱们回来了一致的封装成果集ResponseInfo
但是后面一切的controller
都这么写不太友好。
- 回来内容这么不够清晰详细
- 一切
controller
这么写添加重复工作量
咱们能够这么去优化:
Spirng 供给了 ResponseBodyAdvice
接口,支持在音讯转化器执行转化之前,对接口的回来成果进行处理,再结合 @ControllerAdvice
注解即可轻松支持上述功用。
二、一致呼应装备
准备文件:
ResponseCodeEnums.java 呼应类型枚举类
ResponseInfo.java 呼应数据封装类
ResponseResultHandler.java 呼应信息增强类
开端
ResponseCodeEnums.java
@Getter
@AllArgsConstructor
public enum ResponseCodeEnums {
SUCCESS(200, "success"),
FAIL(500, "failed"),
HTTP_STATUS_200(200, "ok"),
HTTP_STATUS_400(400, "request error"),
HTTP_STATUS_401(401, "no authentication"),
HTTP_STATUS_403(403, "no authorities"),
HTTP_STATUS_500(500, "server error");
private final int code;
private final String message;
}
ResponseInfo.java
@Data
public class ResponseInfo<T> {
/**
* 状况码
*/
protected int code;
/**
* 呼应信息
*/
protected String message;
/**
* 回来数据
*/
private T data;
public static <T> ResponseInfo<T> success() {
return new ResponseInfo<>();
}
public static <T> ResponseInfo<T> success(T data) {
return new ResponseInfo<>(data);
}
public static <T> ResponseInfo<T> fail(String message) {
return new ResponseInfo<>(ResponseCodeEnums.FAIL.getCode(), message);
}
public ResponseInfo() {
this.code = ResponseCodeEnums.SUCCESS.getCode();
this.message = ResponseCodeEnums.SUCCESS.getMessage();
}
public ResponseInfo(ResponseCodeEnums statusEnums) {
this.code = statusEnums.getCode();
this.message = statusEnums.getMessage();
}
/**
* 若没有数据回来,能够人为指定状况码和提示信息
*/
public ResponseInfo(int code, String msg) {
this.code = code;
this.message = msg;
}
/**
* 有数据回来时,状况码为200,默许提示信息为“操作成功!”
*/
public ResponseInfo(T data) {
this.data = data;
this.code = ResponseCodeEnums.SUCCESS.getCode();
this.message = ResponseCodeEnums.SUCCESS.getMessage();
}
/**
* 有数据回来,状况码为 200,人为指定提示信息
*/
public ResponseInfo(T data, String msg) {
this.data = data;
this.code = ResponseCodeEnums.SUCCESS.getCode();
this.message = msg;
}
}
ResponseResultHandler.java
@Slf4j
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
log.info("supports:{}", returnType.getDeclaringClass().getName());
/**
* 扫除swagger-ui恳求回来数据增强
*/
return !returnType.getDeclaringClass().getName().contains("springfox");
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 如果是ResponseInfo数据类型就直接回来
if (body instanceof ResponseInfo) {
return body;
}
// 如果是空,则回来成功
else if (body == null) {
return ResponseInfo.success();
}
// 如果是反常类型就直接回来
else if (body instanceof Exception) {
return body;
}
// 如果是String类型则直接回来String类型
else if (body instanceof String) {
return body;
}
// 回来封装后的数据
else {
return ResponseInfo.success(body);
}
}
}
测验
@RestController
public class TestController {
@GetMapping("/test")
public void hello(){}
}
成果
{
"code": 200,
"message": "success",
"data": null
}
OK,成功地呼应了成果,到此装备完结,下面开端装备一致反常处理。
三、全局反常装备
在日常开发中,咱们经过try…catch的形式捕获反常,并进行处理。在SpringBoot中,咱们能够经过RestControllerAdvice注解来界说全局反常处理,这样就无需每处都try…catch了。
示例:
@Slf4j
@RestControllerAdvice
public class ExceptionHandlerAdvice {
/**
* 参数格局反常处理
*/
@ExceptionHandler({IllegalArgumentException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseInfo<String> badRequestException(IllegalArgumentException ex) {
log.error("参数格局不合法:{}", ex.getMessage());
return new ResponseInfo<>(HttpStatus.BAD_REQUEST.value() + "", "参数格局不符!");
}
/**
* 权限不足反常处理
*/
@ExceptionHandler({AccessDeniedException.class})
@ResponseStatus(HttpStatus.FORBIDDEN)
public ResponseInfo<String> badRequestException(AccessDeniedException ex) {
return new ResponseInfo<>(HttpStatus.FORBIDDEN.value() + "", ex.getMessage());
}
/**
* 参数缺失反常处理
*/
@ExceptionHandler({MissingServletRequestParameterException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseInfo<String> badRequestException(Exception ex) {
return new ResponseInfo<>(HttpStatus.BAD_REQUEST.value() + "", "缺少必填参数!");
}
/**
* 空指针反常
*/
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseInfo<String> handleTypeMismatchException(NullPointerException ex) {
log.error("空指针反常,{}", ex.getMessage());
return ResponseInfo.fail("空指针反常");
}
@ExceptionHandler(Exception.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseInfo<String> handleUnexpectedServer(Exception ex) {
log.error("体系反常:", ex);
return ResponseInfo.fail("体系产生反常,请联系管理员");
}
/**
* 体系反常处理
*/
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseInfo<String> exception(Throwable throwable) {
log.error("体系反常", throwable);
return new ResponseInfo<>(HttpStatus.INTERNAL_SERVER_ERROR.value() + "体系反常,请联系管理员!");
}
}
在上述办法中,对一些常见的反常进行了一致处理。一般情况下,根据事务需要还会界说事务反常,并对事务反常进行处理,我们能够根据自己项目中反常的运用情况进行拓宽。
关于@RestControllerAdvice的几点阐明:
- @RestControllerAdvice注解包含了@Component注解,会把被注解的类作为组件交给Spring来管理。
- @RestControllerAdvice注解包含了@ResponseBody注解,反常处理完之后给调用方输出一个JSON格局的封装数据。
- @RestControllerAdvice注解有一个basePackages属性,该属性用来阻拦哪个包中的反常信息,一般不指定,阻拦项目工程中的一切反常。
- 在办法上经过@ExceptionHandler注解来指定详细的反常,在办法中处理该反常信息,最终将成果经过一致的JSON结构体回来给调用者。
测验:
@RestController
public class TestController {
@GetMapping("/test")
public void hello(){
int i = 1 / 0;
}
}
成果:
{
"code": 500,
"message": "体系产生反常,请联系管理员",
"data": null
}
能够看到一致反常处理对空指针反常进行了阻拦处理,并回来了ExceptionHandlerAdvice中界说的一致报文格局。
四、总结
在运用SpringBoot或其他项目中,一致的报文格局和一致的反常处理都是有必要的。本篇文章介绍了根据SpringBoot的完成,如果你的项目中采用了其他的技能栈,则可考虑对应的处理方式。同时,日常中许多类似的功用都能够一致进行处理,防止大量无效的硬编码,提高工作效率。