我正试图找出最简单的方法来控制基本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
。
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);
}
}
最后,在ResponseEntityExceptionHandler
的handleException
方法中添加一个断点可能有助于调试。