【问题标题】:Spring boot - taking control of 404 Not FoundSpring Boot - 控制 404 Not Found
【发布时间】:2015-07-31 12:17:57
【问题描述】:

我正在尝试找出控制基本 Spring Boot RESTful 服务的 404 Not Found 处理程序的最简单方法,例如 Spring 提供的示例:

https://spring.io/guides/gs/rest-service/

而不是让它返回默认的 Json 输出:

{
  "timestamp":1432047177086,
  "status":404,
  "error":"Not Found",
  "exception":"org.springframework.web.servlet.NoHandlerFoundException",
  "message":"No handler found for GET /aaa, ..."
}

我想提供自己的 Json 输出。

通过控制DispatcherServlet 并使用DispatcherServlet#setThrowExceptionIfNoHandlerFound(true),我可以让它在404 的情况下抛出异常,但我无法通过@ExceptionHandler 处理该异常,就像我想要的那样MissingServletRequestParameterException。知道为什么吗?

或者有没有比抛出和处理 NoHandlerFoundException 更好的方法?

【问题讨论】:

  • 可能是this 会对你有所帮助。

标签: java json spring rest http-status-code-404


【解决方案1】:

效果很好。

当你使用SpringBoot时,它不会显式处理(404 Not Found);它使用 WebMvc 错误响应。如果您的 Spring Boot 应该处理该异常,那么您应该围绕 Spring Boot 做一些修改。对于 404,异常类为 NoHandlerFoundException;如果要在 @RestControllerAdvice 类中处理该异常,则必须在 Application 类中添加 @EnableWebMvc 注释并设置 setThrowExceptionIfNoHandlerFound(true); 在 DispatcherServlet 中。请参考以下代码:

@SpringBootApplication
@EnableWebMvc
public class Application {  
    @Autowired
    private DispatcherServlet servlet;

    public static void main(String[] args) throws FileNotFoundException, IOException {
        SpringApplication.run(Application.class, args);
    }
    
    @Bean
    public CommandLineRunner getCommandLineRunner(ApplicationContext context) {
        servlet.setThrowExceptionIfNoHandlerFound(true);
        return args -> {};
    }
}

在此之后,您可以在 @RestControllerAdvice 类中处理 NoHandlerException

@RestControllerAdvice
public class AppException {

    @ExceptionHandler(value={NoHandlerFoundException.class})
    @ResponseStatus(code=HttpStatus.BAD_REQUEST)
    public ApiError badRequest(Exception e, HttpServletRequest request, HttpServletResponse response) {
        e.printStackTrace();
        return new ApiError(400, HttpStatus.BAD_REQUEST.getReasonPhrase());
    }
}   

我创建了 ApiError 类来返回自定义的错误响应

public class ApiError {
    private int code;
    private String message;
    public ApiError(int code, String message) {
        this.code = code;
        this.message = message;
    }
    public ApiError() {
    }   
    //getter & setter methods...
}

【讨论】:

【解决方案2】:

根据Spring documentation appendix A.,有一个名为spring.mvc.throw-exception-if-no-handler-found 的布尔属性可用于启用投掷NoHandlerFoundException。然后,您可以像创建任何其他异常处理程序一样。

@RestControllerAdvice
class MyExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(MyExceptionHandler.class);

    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public String handleNoHandlerFoundException(NoHandlerFoundException ex) {
        log.error("404 situation detected.",ex);
        return "Specified path not found on this server";
    }
}

@ExceptionHandler 本身没有@ControllerAdvice(或@RestControllerAdvice)不能使用,因为它只绑定到它的控制器。

【讨论】:

    【解决方案3】:

    简而言之,NoHandlerFoundException 是从容器中抛出的,而不是从容器中的应用程序中抛出的。因此,您的 Container 无法知道您的 @ExceptionHandler,因为这是 Spring 功能,而不是容器中的任何内容。

    你想要的是HandlerExceptionResolver。我和你有同样的问题,看看我的解决方案:How to intercept "global" 404s on embedded Tomcats in spring-boot

    【讨论】:

      【解决方案4】:

      基于 @EnableWebMvc 的解决方案可以工作,但它可能会破坏 Spring boot 自动配置。 我正在使用的解决方案是实现ErrorController:

      @RestController
      @RequiredArgsConstructor
      public class MyErrorController implements ErrorController {
      
              private static final String ERROR_PATH = "/error";
      
              @NonNull
              private final ErrorAttributes errorAttributes;
      
              @RequestMapping(value = ERROR_PATH)
              Map<String, Object> handleError(WebRequest request) {
                  return errorAttributes.getErrorAttributes(request, false);
              }
      
              @Override
              public String getErrorPath() {
                  return ERROR_PATH;
              }
      }
      

      【讨论】:

        【解决方案5】:

        这个问题的解决方法是:

        • 将 DispatcherServlet 配置为在找不到任何处理程序时抛出异常。
        • 为将从 DispatcherServlet 引发的异常提供您的实现,本例为 NoHandlerFoundException

        因此,为了配置 DispatcherServlet,您可以使用属性文件或 Java 代码。
        properties.yaml 的示例,

        spring:
            mvc:
              throw-exception-if-no-handler-found: true
        

        properties.properties 的示例,

        spring.mvn.throw-exception-if-no-handler-found=true
        

        Java代码示例,我们只是想在启动时运行命令servlet.setThrowExceptionIfNoHandlerFound(true);,我使用InitializingBean接口,您可以使用其他方式。我从baeldung 找到了一篇写得很好的指南,可以在春季启动时运行逻辑。

        @Component
        public class WebConfig implements InitializingBean {
        
            @Autowired
            private DispatcherServlet servlet;
        
            @Override
            public void afterPropertiesSet() throws Exception {
                servlet.setThrowExceptionIfNoHandlerFound(true);
            }
        
        }
        

        小心! 在 Spring Boot 2 中添加 @EnableWebMvc 会禁用自动配置,这意味着如果您使用注解 @EnableWebMvc 那么您应该使用 Java 代码示例,因为 spring.mvc.* 属性不会有任何效果。

        配置 DispatcherServlet 后,您应该覆盖在抛出异常时调用的 ResponseEntityExceptionHandler。我们希望在抛出 NoHandlerFoundException 时覆盖该操作,如下例所示。

        @ControllerAdvice
        public class MyApiExceptionHandler extends ResponseEntityExceptionHandler {
        
            @Override
            public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
                String responseBody = "{\"errormessage\":\"WHATEVER YOU LIKE\"}";
                headers.add("Content-Type", "application/json;charset=utf-8");
                return handleExceptionInternal(ex, responseBody, headers, HttpStatus.NOT_FOUND, request);
            }
        }
        

        最后,在ResponseEntityExceptionHandler 的方法handleException 中添加断点可能有助于调试。

        【讨论】:

        • spring.resources.add-mappings=false 我必须在spring boot中为404添加这个
        猜你喜欢
        • 2020-10-21
        • 2018-04-07
        • 2018-12-10
        • 1970-01-01
        • 2021-09-11
        • 2020-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多