携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
起因
SpringMVC统一错误处理
该功能可以直接通过SpringMVC的@RestControllerAdvice注解搭配@ExceptionHandler来处理。
例如:
@RestControllerAdvice
public class ExceptionResponseAdvice {
@ExceptionHandler(IndexOutOfBoundsException.class)
public Exception handleException(Exception exception){
System.out.println("数组越界错误!");
//handle Exception
return exception;
}
@ExceptionHandler(ClassNotFoundException.class)
public Exception handleException(Exception exception){
System.out.println("类未找到错判!");
//handle Exception
return exception;
}
@ExceptionHandler(Throwable.class)
public Exception handleException(Exception exception){
System.out.println("兜底所有错误!");
//handle Exception
return exception;
}
}
springMVC统一返回结构
同样使用@RestControllerAdvice可以用来统一数据的返回结构。不过我们需要实现ResponseBodyAdvice接口。才能劫持方法返回的数据,然后进行重写其中的方法。
例如:
@RestControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {
ObjectMapper mapper = new ObjectMapper();
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//处理返回值是String的状况
//处理字符串类型数据
if(o instanceof String){
try {
return mapper.writeValueAsString(Result.data(o));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
// 处理错误
if(o instanceof Exception){
return Result.getResult((Exception) o);
}
return o instanceof Result ? o : Result.data(o);
}
}
这里还有一个坑,就是json化和String类型的冲突,即如果Controller返回的是一个String。不加这一句的话会报错:
class priv.mw.utils.Result cannot be cast to class java.lang.String (priv.mw.utils.Result is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader @5049b1e5; java.lang.String is in module java.base of loader 'bootstrap')
即没办法将Result类型转化为String。其本质原因在于由于返回的是String,所以会匹配到StringHttpMessageConverter,因为他在所有Converter的前面。但由于我们在切面中将类型改成了Result,所以就会报错。
但实际上我们需要的是MappingJackson2HttpMessageConverter。
所以目前的解决办法有两种:
- 在转换之前就将String转化为JSON字符串。
- 将
MappingJackson2HttpMessageConverter放在所有converter的首位。
第一种就是就是上面的写法。
第二种可以通过配置来实现:
-
XML方式:
<mvc:annotation-driven> <mvc:message-converters> <!-- 将MappingJackson2HttpMessageConverter移到前面,处理ResponseBodyAdvice的String异常的问题--> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="defaultCharset" value="UTF-8"/> </bean> </mvc:message-converters> </mvc:annotation-driven>-
Class方式
import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class WebConfiguration implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(0, new MappingJackson2HttpMessageConverter()); } }
-
还有一点值得注意的是:实际上错误处理是在ResponseBodyAdvice中,即统一结构返回中来处理。因为实际上我们需要统一返回结构,对于错误,需要判断错误类型来返回对应的数据。所以在这里处理是更加方便的。
漏掉的404
虽然上面的做到了统一错误处理,但是404地址错误仍然没办法被劫持到。
目前的做法是,实现一个能够劫持所有请求地址的controller,因为其在springMVC中,泛匹配优先级最低。所以可以这样实现。
@RestController
public class Error {
@RequestMapping("/**")
public String NotFound() throws ClientException {
throw new ClientException("请求地址出错!");
}
}
后期看能不能有更优的处理方法。