rest接口_SpringBoot 全局异常处理+Rest接口返回统一数据格式最佳实践

244176119b0312127111c9142acc0cc5.png

先看下目录结构

13dc3c50a50816bcb9e0f7cd30ea90a1.png

1. 统一返回类型结构

1.1 为什么要统一格式?

我们使用SpringBoot编写接口的时候,最好是返回一个统一格式的JSON,该格式包含错误码,附带信息,以及携带的数据。这样前端在解析的时候就能统一解析,同时携带错误码可以更加容易的排查错误。

1.2 pom文件

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.boot

spring-boot-starter-parent

2.1.7.RELEASE

com.example

log-demo

0.0.1-SNAPSHOT

log-demo

Demo project for Spring Boot

1.8

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.projectlombok

lombok

1.16.20

provided

com.alibaba

fastjson

1.2.47

org.apache.commons

commons-lang3

3.8.1

org.aspectj

aspectjrt

1.9.2

org.aspectj

aspectjweaver

1.9.2

org.springframework.boot

spring-boot-devtools

true

org.springframework.boot

spring-boot-maven-plugin

org.springframework.boot

spring-boot-maven-plugin

true

1.3 新建 BaseResponse 定义统一的系统基本返回类型

public class BaseResponse {

private int code;

private String message;

private T data;

public BaseResponse(int code, String message, T data) {

this.code = code;

this.message = message;

this.data = data;

}

public BaseResponse(T data, ErrorType errorType){

this.code=errorType.getCode();

this.message=errorType.getMessage();

this.data=data;

}

public BaseResponse(ErrorType errorType){

this.code=errorType.getCode();

this.message=errorType.getMessage();

}

public int getCode() {

return code;

}

public void setCode(int code) {

this.code = code;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

public T getData() {

return data;

}

public void setData(T data) {

this.data = data;

}

}

1.4 新建 BaseResponseBodyAdvice类 对restcontroller的body体进行统一返回

@ControllerAdvice

public class BaseResponseBodyAdvice implements ResponseBodyAdvice {

/**

* 这个方法表示对于哪些请求要执行beforeBodyWrite,返回true执行,返回false不执行

*/

@Override

public boolean supports(MethodParameter methodParameter, Class extends HttpMessageConverter>> aClass) {

return true;

}

/**

* 对于返回的对象如果不是最终对象ResponseResult,则选包装一下

*/

@Override

public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class extends HttpMessageConverter>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {

/**

* 验证方面的特殊处理

*/

HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

if(httpServletRequest.getAttribute(ErrorType.class.getSimpleName()) != null){

System.out.println(ErrorType.class.getSimpleName());

ErrorType errorType = (ErrorType)httpServletRequest.getAttribute(ErrorType.class.getSimpleName());

return JSONObject.toJSON(new BaseResponse<>(errorType));

}

//如果是字符类型,输出json字符串

if(mediaType.includes(MediaType.TEXT_HTML)||mediaType.includes(MediaType.TEXT_PLAIN)){

return JSONObject.toJSON(new BaseResponse<>(body, ErrorType.SUCCESS));

}

//如果已经被异常捕获,返回的就是BaseResponse对象,不用再次封装了

if (body instanceof BaseResponse>) {

return body;

}

return new BaseResponse<>(body, ErrorType.SUCCESS);

}

}

1.5 新建 User 模型类 待会做测试用

@Data

@AllArgsConstructor

public class User {

private String userName;

@NotNull(message = "年龄不能为空!")

private Integer Age;

}

1.6 测试

@RestController

@RequestMapping("test")

@Slf4j

public class TestController {

/**

* 测试Validated 后端参数校验

*/

@PostMapping("/addUser")

public User testPost(@RequestBody @Validated User user){

return user;

}

/**

* 返回对象信息

*/

@GetMapping("/getUser")

public User getUser(){

User user=new User("liuzongqiang",22);

return user;

}

/**

* 返回字符串

*/

@GetMapping("/getStr")

public String getStr(){

return "hello";

}

}

结果:

579cc8d2a68307e8a526deb9693b11a8.png

在返回string类型时报:com.alibaba.fastjson.JSONObject cannot be cast to java.lang.String 配置一个消息转换器就可以了

2. 统一异常处理

2.1 新建一个ErrorType 枚举类,用于定义系统错误类型

public enum ErrorType {

//资源参数问题

PARAM_INVALID(-1, "参数不合法"),

NOT_FOUND(2, "不存在此资源"),

ALREADY_EXISTS(3, "资源已经存在,不能重复"),

PARAMS_NULL(3, "参数不能为空"),

//身份方面问题

REQUEST_OUT_OF_TIME(4, "请求过期"),

AUTHENTICATION_FAILED(100, "身份认证失败"),

REQUEST_METHOD_ERROR(101, "请求类型不支持"),

OPERATION_SCOPE_FAILED(102, "不支持此操作"),

//系统基本码

ERROR(0, "系统异常"),

SUCCESS(1, "成功");

private int code;

private String message;

ErrorType(int code, String message) {

this.code = code;

this.message = message;

}

public int getCode() {

return code;

}

public void setCode(int code) {

this.code = code;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

}

2.2 新建 CustomException 自定义异常类

public class CustomException {

@Data

@Accessors(chain = true)

@AllArgsConstructor

public static class ParamInvalidException extends RuntimeException{

private Map params;

}

@Data

@Accessors(chain = true)

@AllArgsConstructor

public static class NotFoundException extends RuntimeException{

private Map params;

}

@Data

@Accessors(chain = true)

@AllArgsConstructor

public static class AlreadyExistsException extends RuntimeException{

private Map params;

}

@Data

@Accessors(chain = true)

@AllArgsConstructor

public static class ParamNullException extends RuntimeException{

private Map params;

}

}

2.3 新建 BaseControllerAdvice 全局异常处理控制器

/**

* controller异常拦截,关键应用可从异常中获取有用信息并做日志记录

*/

@ControllerAdvice

public class BaseControllerAdvice {

@ResponseBody

@ExceptionHandler(Exception.class)

public BaseResponse sysError(Exception e){

return new BaseResponse<>(null,ErrorType.ERROR.getCode(),e.getMessage()==null?String.valueOf(e):ErrorType.ERROR.getMessage());

}

@ResponseBody

@ExceptionHandler(IllegalArgumentException.class)

public BaseResponse argumentError(IllegalArgumentException e){

return new BaseResponse<>(null,ErrorType.PARAM_INVALID.getCode(),e.getLocalizedMessage()==null?String.valueOf(e):e.getLocalizedMessage());

}

@ResponseBody

@ExceptionHandler(MethodArgumentNotValidException.class)

public BaseResponse validateException(MethodArgumentNotValidException e){

final List errList = new ArrayList<>();

e.getBindingResult().getAllErrors().stream().forEach(x-> {

errList.add(x.getDefaultMessage());

});

return new BaseResponse<>("",ErrorType.PARAM_INVALID.getCode(),errList.toString());

}

@ResponseBody

@ExceptionHandler(ParamInvalidException.class)

public BaseResponse> paramInvalidException(ParamInvalidException e){

return new BaseResponse<>(e.getParams(),ErrorType.PARAM_INVALID);

}

@ResponseBody

@ExceptionHandler(NotFoundException.class)

public BaseResponse> notFoundException(NotFoundException e){

return new BaseResponse<>(e.getParams(),ErrorType.NOT_FOUND);

}

@ResponseBody

@ExceptionHandler(AlreadyExistsException.class)

public BaseResponse> alreadyExistsException(AlreadyExistsException e){

return new BaseResponse<>(e.getParams(),ErrorType.ALREADY_EXISTS);

}

@ResponseBody

@ExceptionHandler(ParamNullException.class)

public BaseResponse> paramNullException(ParamNullException e){

return new BaseResponse<>(e.getParams(),ErrorType.PARAMS_NULL);

}

@ResponseBody

@ExceptionHandler(ImageUploadException.class)

public BaseResponse> imageUploadException(ImageUploadException e){

return new BaseResponse<>(e.getOriginFileUrl(),ErrorType.IMG_UPLOAD_ERR);

}

@ResponseBody

@ExceptionHandler(ImageNullException.class)

public BaseResponse> imageNullException(ImageNullException e){

return new BaseResponse<>(e.getParams(),ErrorType.IMG_NOT_NULL);

}

@ResponseBody

@ExceptionHandler(OssImageFailDelException.class)

public BaseResponse> ossImageFailDelException(OssImageFailDelException e){

return new BaseResponse<>(e.getParams(),ErrorType.OSS_FAIL_DEL);

}

@ResponseBody

@ExceptionHandler(InventoryUnEnoughException.class)

public BaseResponse> inventoryUnEnough(InventoryUnEnoughException e){

return new BaseResponse<>(e.getParams(),ErrorType.PRODUCT_INVENTORY_UNENOUGH);

}

}

2.4 测试

@RestController

@RequestMapping("test")

@Slf4j

public class TestController {

@GetMapping("")

public String test(){

Map map=new HashMap<>();

map.put("id","12");

throw new CustomException.NotFoundException(map);

}

}

结果:

bda5ba0d552a872741cdd286483a2119.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值