弹簧启动控制404未找到



我正试图找出最简单的方法来控制基本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更好的方法吗?

效果很好。

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

@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...
}

根据Spring文档附录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";
    }
}
没有@ControllerAdvice(或@RestControllerAdvice)的

@ExceptionHandler本身不能使用,因为它只绑定到它的控制器。

简而言之,NoHandlerFoundException是从容器中抛出的,而不是从容器中的应用程序抛出的。因此,你的容器没有办法知道你的@ExceptionHandler,因为这是一个Spring功能,而不是来自容器的任何东西。

你想要的是HandlerExceptionResolver。我和你有同样的问题,看看我的解决方案:如何拦截"全球"。

基于@EnableWebMvc的解决方案可以工作,但它可能会破坏Spring启动自动配置。我使用的解决方案是实现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;
        }
}

这个问题的解决方案是:

  • 配置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找到了一个写得很好的关于spring启动时运行逻辑的指南。

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

小心!添加@EnableWebMvc会禁用Spring Boot 2中的自动配置,这意味着如果你使用@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);
    }
}

最后,在ResponseEntityExceptionHandlerhandleException方法中添加一个断点可能有助于调试。

最新更新