今天小编给大家分享一下SpringBoot统一接口返回及全局异常怎么处理的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
网站制作、建网站找专业的建站公司成都创新互联 :定制网站、模板网站、仿站、小程序制作 、软件开发、app软件定制开发 等。做网站价格咨询成都创新互联 :服务完善、10余年建站、值得信赖!网站制作电话:13518219792
一、SpringBoot不使用统一返回格式 默认情况下,SpringBoot会有如下三种返回情况。
1.1 使用字符串返回 @GetMapping("/getUserName")
public String getUserName(){
return "HuaGe";
} 调用接口返回结果:
HuaGe 1.2 使用实体类返回 @GetMapping("/getUserName")
public User getUserName(){
return new User("HuaGe",18,"男");
} 调用接口返回结果:
{
"name": "HuaGe",
"age": "18",
"性别": "男",
} 1.3 异常情况下返回 @GetMapping("/getUserName")
public static String getUserName(){
HashMap hashMap = Maps.newHashMap();
return hashMap.get(0).toString();
} 模拟一个空指针异常,在不做任何异常处理的情况下,可以看下SpringBoot的默认返回结果:
{
"timestamp": "2021-08-09T06:56:41.524+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/sysUser/getUserName"
} 对于上面这几种情况,如果整个项目没有定义统一的返回格式,五个后台开发人员定义五种返回格式,这样不仅代码臃肿,前后端对接效率低,而且还会有一些意向不到的情况发生,比如前端直接显示异常详情等,这给用户的体验是非常差的。
二、基础玩法 项目中最常见到的是封装一个工具类,类中定义需要返回的字段信息,把需要返回前端的接口信息,通过该类进行封装,这样就可以解决返回格式不统一的现象了。
2.1 参数说明 2.2 流程说明 public class Result {
private int code;
private String message;
private T data;
public Result() {}
public Result(int code, String message) {
this.code = code;
this.message = message;
}
/**
* 成功
*/
public static Result success(T data) {
Result result = new Result();
result.setCode(ResultMsgEnum.SUCCESS.getCode());
result.setMessage(ResultMsgEnum.SUCCESS.getMessage());
result.setData(data);
return result;
}
/**
* 失败
*/
public static Result error(int code, String message) {
return new Result(code, message);
}
} 定义返回状态码
public enum ResultMsgEnum {
SUCCESS(0, "成功"),
FAIL(-1, "失败"),
AUTH_ERROR(502, "授权失败!"),
SERVER_BUSY(503, "服务器 正忙,请稍后再试!"),
DATABASE_OPERATION_FAILED(504, "数据库操作失败");
private int code;
private String message;
ResultMsgEnum(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
} 上面两步定义了数据返回格式
和状态码
,接下来就要看下在接口中如何使用了。
@GetMapping("/getUserName")
public Result getUserName(){
return Result.success("huage");
} 调用结果如下,可以看到是我们在Result中定义的参数类型。
{
"code": 0,
"message": "成功",
"data": "huage"
} 这样写虽然能够满足日常需求,而且我相信很多小伙伴也是这么用的,但是如果我们有大量的接口,然后在每一个接口中都使用Result.success
来包装返回信息,会新增很多重复代码,显得不够优雅,甚至都不好意思拿出去显摆。 肯定会有一种方式能够再一次提高代码逼格,实现最优解。
三、进阶用法 基本用法学会后,接下来看点究极版本,主要用到如下两个知识点,用法简单,无论是拿出来教学妹,还是指点小姐姐,都是必备技能。
3.1 类介绍 3.2 用法说明 @RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
/**
* 是否开启功能 true:是
*/
@Override
public boolean supports(MethodParameter methodParameter, Class> aClass) {
return true;
}
/**
* 处理返回结果
*/
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//处理字符串类型数据
if(o instanceof String){
try {
return objectMapper.writeValueAsString(Result.success(o));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return Result.success(o);
}
}我们可以通过getUserName
接口测试一下,会发现和直接使用Result
返回的结果是一致的。
不过,细心的小伙伴们肯定注意到了,在ResponseAdvice
我们全部使用了Result.success(o)
来处理结果,对于error类型的结果未做处理。我们来看下,发生异常情况时,返回结果是什么样呢?继续使用上面HashMap空指针异常的代码,测试结果如下:
{
"code": 0,
"message": "成功",
"data": {
"timestamp": "2021-08-09T09:33:26.805+00:00",
"status": 405,
"error": "Method Not Allowed",
"path": "/sysUser/getUserName"
}
} 虽然格式上没有毛病,但是在code、data字段的具体数据上是不友好或不正确的。不处理好这些事情,会严重影响自己在前端妹妹心中的高大形象的,这是决不能容忍的。
3.3 全局异常处理器 以前我们遇到异常时,第一时间想到的应该是try..catch..finnal吧,不过这种方式会导致大量代码重复,维护困难,逻辑臃肿等问题,这不是我们想要的结果。
今天我们要用的全局异常处理方式,用起来是比较简单的。首先新增一个类,增加@RestControllerAdvice
注解,该注解的作用花哥上面已经介绍过,就不再唠叨了。
@RestControllerAdvice
public class CustomerExceptionHandler {
} 如果我们有想要拦截的异常类型,就新增一个方法,使用@ExceptionHandler
注解修饰,注解参数为目标异常类型。
例如:controller中接口发生Exception异常时,就会进入到Execption
方法中进行捕获,将杂乱的异常信息,转换成指定格式后交给ResponseAdvice
方法进行统一格式封装并返回给前端小伙伴。
@RestControllerAdvice
@Slf4j
public class CustomerExceptionHandler {
@ExceptionHandler(AuthException.class)
public String ErrorHandler(AuthorizationException e) {
log.error("没有通过权限验证!", e);
return "没有通过权限验证!";
}
@ExceptionHandler(Exception.class)
public Result Execption(Exception e) {
log.error("未知异常!", e);
return Result.error(ResultMsgEnum.SERVER_BUSY.getCode(),ResultMsgEnum.SERVER_BUSY.getMessage());
}
} 再次调用接口getUserName
查看返回结果,会发现还是有一些问题,因为我们在CustomerExceptionHandler
中已经将接口返回结果封装成Result
类型,而代码执行到统一结果返回类ResponseAdvice
时,又会结果再次封装,就出现了如下问题。
{
"code": 0,
"message": "成功",
"data": {
"code": 503,
"message": "服务器正忙,请稍后再试!",
"data": null
}
} 3.4 统一返回结果处理类最终版 解决上述问题非常简单,只要在beforeBodyWrite
中增加一条判断即可。
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
/**
* 是否开启功能 true:开启
*/
@Override
public boolean supports(MethodParameter methodParameter, Class> aClass) {
return true;
}
/**
* 处理返回结果
*/
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//处理字符串类型数据
if(o instanceof String){
try {
return objectMapper.writeValueAsString(Result.success(o));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
//返回类型是否已经封装
if(o instanceof Result){
return o;
}
return Result.success(o);
}
}以上就是“SpringBoot统一接口返回及全局异常怎么处理”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注创新互联行业资讯频道。
名称栏目:SpringBoot统一接口返回及全局异常怎么处理
URL标题:http://csdahua.cn/article/iijiig.html
扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流