UNSUPPORTED_GRANT_TYPE(1003, “不支持的认证模式”);
/自定义状态码/
private final int code;
/自定义描述/
private final String message;
ReturnCode(int code, String message){
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
复制代码
统一返回格式
@GetMapping(“/hello”)
public ResultData getStr(){
return ResultData.success(“hello,javadaily”);
}
复制代码
此时调用接口获取到的返回值是这样:
{
“status”: 100,
“message”: “hello,javadaily”,
“data”: null,
“timestamp”: 1625736481648,
“httpStatus”: 0
}


这样确实已经实现了我们想要的结果,我在很多项目中看到的都是这种写法,在Controller层通过 ResultData.success()对返回结果进行包装后返回给前端。

看到这里我们不妨停下来想想,这样做有什么弊端呢?

最大的弊端就是我们后面每写一个接口都需要调用 ResultData.success()这行代码对结果进行包装,重复劳动,浪费体力;而且还很容易被其他老鸟给嘲笑。

Springboot页面怎么返回403 springboot返回网页_java

所以呢我们需要对代码进行优化,目标就是不要每个接口都手工制定 ResultData返回值。

高级实现方式

要优化这段代码很简单,我们只需要借助SpringBoot提供的 ResponseBodyAdvice即可。

ResponseBodyAdvice的作用:拦截Controller方法的返回值,统一处理返回值/响应体,一般用来统一返回格式,加解密,签名等等。

先来看下 ResponseBodyAdvice的源码:

public interface ResponseBodyAdvice {
/**
• 是否支持advice功能
• true 支持,false 不支持
*/
boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
/**
• 对返回的数据进行处理
*/
@Nullable
T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}


我们只需要编写一个具体实现类即可

/**
• @author jam
• @date 2021/7/8 10:10 上午
*/
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if(o instanceof String){
return objectMapper.writeValueAsString(ResultData.success(o));
}
return ResultData.success(o);
}
}

复制代码

需要注意两个地方:

  • @RestControllerAdvice注解

@RestControllerAdvice@RestController注解的增强,可以实现三个方面的功能:

  1. 全局异常处理
  2. 全局数据绑定
  3. 全局数据预处理
  • String类型判断
if(o instanceof String){
return objectMapper.writeValueAsString(ResultData.success(o));
}


这段代码一定要加,如果Controller直接返回String的话,SpringBoot是直接返回,故我们需要手动转换成json。

经过上面的处理我们就再也不需要通过 ResultData.success()来进行转换了,直接返回原始数据格式,SpringBoot自动帮我们实现包装类的封装。

@GetMapping(“/hello”)
public String getStr(){
return “hello,javadaily”;
}

复制代码

此时我们调用接口返回的数据结果为:

{
“status”: 100,
“message”: “操作成功”,
“data”: “hello,javadaily”,
“timestamp”: 1626427373113
}

复制代码

是不是感觉很完美,别急,还有个问题在等着你呢。

Springboot页面怎么返回403 springboot返回网页_Springboot页面怎么返回403_02

接口异常问题

此时有个问题,由于我们没对Controller的异常进行处理,当我们调用的方法一旦出现异常,就会出现问题,比如下面这个接口

@GetMapping(“/wrong”)
public int error(){
int i = 9/0;
return i;
}

复制代码

返回的结果为:

Springboot页面怎么返回403 springboot返回网页_java_03

这显然不是我们想要的结果,接口都报错了还返回操作成功的响应码,前端看了会打人的。

别急,接下来我们进入第二个议题,如何优雅的处理全局异常。

SpringBoot为什么需要全局异常处理器


  1. 不用手写try…catch,由全局异常处理器统一捕获

使用全局异常处理器最大的便利就是程序员在写代码时不再需要手写 try...catch了,前面我们讲过,默认情况下SpringBoot出现异常时返回的结果是这样:

{
“timestamp”: “2021-07-08T08:05:15.423+00:00”,
“status”: 500,
“error”: “Internal Server Error”,
“path”: “/wrong”
}

复制代码

这种数据格式返回给前端,前端是看不懂的,所以这时候我们一般通过

复制代码

try...catch来处理异常

@GetMapping(“/wrong”)
public int error(){
int i;
try{
i = 9/0;
}catch (Exception e){
log.error(“error:{}”,e);
i = 0;
}
return i;
}

复制代码

我们追求的目标肯定是不需要再手动写 try...catch了,而是希望由全局异常处理器处理。

  1. 对于自定义异常,只能通过全局异常处理器来处理
@GetMapping(“error1”)
public void empty(){
throw new RuntimeException(“自定义异常”);
}

复制代码

  1. 当我们引入Validator参数校验器的时候,参数校验不通过会抛出异常,此时是无法用 try...catch捕获的,只能使用全局异常处理器。

SpringBoot集成参数校验请参考这篇文章SpringBoot开发秘籍 - 集成参数校验及高阶技巧

如何实现全局异常处理器

@Slf4j
@RestControllerAdvice
public class RestExceptionHandler {
/**
• 默认全局异常处理。
• @param e the e
• @return ResultData
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResultData exception(Exception e) {
log.error(“全局异常信息 ex={}”, e.getMessage(), e);
return ResultData.fail(ReturnCode.RC500.getCode(),e.getMessage());
}
}