我使用Spring启动来托管REST API。我希望始终发送JSON响应,而不是标准的错误响应,即使浏览器正在访问URL以及自定义数据结构。
我可以使用@ControllerAdvice和@ExceptionHandler来执行自定义异常。但我无法找到任何好的方法来处理标准和处理错误,如404和401。
有没有什么好的模式可以做到这一点?
答案 0 :(得分:4)
404错误由DispatcherServlet处理。有一个属性throwExceptionIfNoHandlerFound,你可以覆盖它。
在Application类中,您可以创建一个新bean:
@Bean
DispatcherServlet dispatcherServlet () {
DispatcherServlet ds = new DispatcherServlet();
ds.setThrowExceptionIfNoHandlerFound(true);
return ds;
}
...然后在
中捕获NoHandlerFoundException异常@EnableWebMvc
@ControllerAdvice
public class GlobalControllerExceptionHandler {
@ExceptionHandler
@ResponseStatus(value=HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorMessageResponse requestHandlingNoHandlerFound(final NoHandlerFoundException ex) {
doSomething(LOG.debug("text to log"));
}
}
答案 1 :(得分:3)
我提供了有关如何覆盖404案例响应的示例解决方案。解决方案非常简单,我发布了示例代码,但您可以在原始帖子中找到更多详细信息:Spring Boot Rest - How to configure 404 - resource not found
首先:定义将处理错误案例并覆盖响应的控制器:
@ControllerAdvice
public class ExceptionHandlerController {
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(value= HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse requestHandlingNoHandlerFound() {
return new ErrorResponse("custom_404", "message for 404 error code");
}
}
第二次:你需要告诉Spring在404的情况下抛出异常(无法解析处理程序):
@SpringBootApplication
@EnableWebMvc
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
}
}
答案 2 :(得分:3)
在@RestControllerAdvice with spring boot 的情况下对我有用
spring.mvc.throw-exception-if-no-handler-found=true
server.error.whitelabel.enabled=false
spring.resources.add-mappings=false
@RestControllerAdvice
public class ErrorHandlerController {
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND )
public String handleNotFoundError(NoHandlerFoundException ex) {
return "path does not exists";
}
}
答案 3 :(得分:2)
总结所有答案和评论,我认为做到这一点的最佳方法是-
首先,告诉Spring Boot在application.properties
中找不到处理程序的情况下引发异常。
spring.mvc.throw-exception-if-no-handler-found=true
然后在应用程序中处理NoHandlerFoundException
。我按照以下方式处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NoHandlerFoundException.class)
public void handleNotFoundError(HttpServletResponse response, NoHandlerFoundException ex) {
ErrorDto errorDto = Errors.URL_NOT_FOUND.getErrorDto();
logger.error("URL not found exception: " + ex.getRequestURL());
prepareErrorResponse(response, HttpStatus.NOT_FOUND, errorDto);
}
}
答案 4 :(得分:1)
您可以添加与web.xml中的错误页面定义相关的自定义 ErrorPage 对象。 Spring Boot provides an example ...
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer(){
return new MyCustomizer();
}
// ...
private static class MyCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html"));
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/not-found.html"));
}
}
编辑虽然我认为如果您将错误页面设置为控制器,上面的方法将会起作用,但更简单的方法是包含一个自定义的 ErrorController 下面...
@Bean
public ErrorController errorController(ErrorAttributes errorAttributes) {
return new CustomErrorController(errorAttributes);
}
// ...
public class CustomErrorController extends BasicErrorController {
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@Override
@RequestMapping(value = "${error.path:/error}")
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
ResponseEntity<Map<String, Object>> error = super.error(request);
HttpStatus statusCode = error.getStatusCode();
switch (statusCode) {
case NOT_FOUND:
return getMyCustomNotFoundResponseEntity(request);
case UNAUTHORIZED:
return getMyCustomUnauthorizedResponseEntity(request);
default:
return error;
}
}
}
答案 5 :(得分:1)
对于那些不想使用@EnableWebMvc
的Spring Boot 2用户
application.properties
server.error.whitelabel.enabled=false
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
ControllerAdvice
@RestControllerAdvice
public class ExceptionResolver {
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public HashMap<String, String> handleNoHandlerFound(NoHandlerFoundException e, WebRequest request) {
HashMap<String, String> response = new HashMap<>();
response.put("status", "fail");
response.put("message", e.getLocalizedMessage());
return response;
}
}
答案 6 :(得分:1)
您可以扩展 ResponseEntityExceptionHandler
类,其中包含 Spring Boot 项目中的许多常见异常。例如,如果您希望使用自定义处理程序来绑定异常,您可以使用以下内容,
@ControllerAdvice
public class MyApiExceptionHandler extends ResponseEntityExceptionHandler {
@Override
public ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
String responseBody = "{\"key\":\"value\"}";
headers.add("Content-Type", "application/json;charset=utf-8");
return handleExceptionInternal(ex, responseBody, headers, HttpStatus.NOT_ACCEPTABLE, request);
}
}
http 状态的另一个示例 404-Not Found,
@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);
}
}
关于 404 not found 异常,您应该将 DispatcherServlet 配置为在未找到任何处理程序时抛出异常,而不是默认行为。对于 404 问题,您还可以阅读this 问题。
答案 7 :(得分:0)
您似乎需要引入适当注释的方法,例如:对于不支持的媒体类型(415),它将是:
@ExceptionHandler(MethodArgumentNotValidException)
public ResponseEntity handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) {
logger.error('Caught exception', e)
def response = new ExceptionResponse(
error: 'Validation error',
exception: e.class.name,
message: e.bindingResult.fieldErrors.collect { "'$it.field' $it.defaultMessage" }.join(', '),
path: req.servletPath,
status: BAD_REQUEST.value(),
timestamp: currentTimeMillis()
)
new ResponseEntity<>(response, BAD_REQUEST)
}
然而,可能无法实现,因为401和404可能会在到达DispatcherServlet
之前被抛出 - 在这种情况下ControllerAdvice
将不起作用。
答案 8 :(得分:0)
请参阅Spring Boot REST service exception handling。它显示了如何告诉dispatcherservlet为“找不到路由”发出异常,然后告诉如何捕获这些异常。我们(我工作的地方)现在正在生产我们的REST服务。
答案 9 :(得分:0)
从Spring版本5开始,可以使用类ResponseStatusException:
@GetMapping("example")
public ResponseEntity example() {
try {
throw new MyException();
} catch (MyException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "My Exception", e);
}
}
答案 10 :(得分:-1)
我希望在所有可能的错误情况下都具有相同的错误格式(json)结构,因此我只是重用了AbstractErrorController的代码,注册了自己的ErrorController:
@Controller
@RequestMapping(path = "/error", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public class ErrorController extends AbstractErrorController {
public ErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers.orderedStream().collect(Collectors.toUnmodifiableList()));
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
final var status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
return new ResponseEntity<>(getErrorAttributes(request, ErrorAttributeOptions.defaults()), status);
}
@Override
public String getErrorPath() {
return null;
}
}
与此相关,您不需要任何控制器建议,默认情况下所有错误都将转到错误方法