⛔springMVC中404错误的拦截

534 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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("请求地址出错!");
    }
}

后期看能不能有更优的处理方法。