我使用Spring MVC和Spring Boot以及Thymeleaf。我有普通的控制器,它们返回一个Thymeleaf模板的名称和用@RepsonseBody
注释的REST控制器。
假设我有一个EntityNotFoundException
被控制器调用的某些代码抛出。如果它被抛出,我想分别为REST控制器返回404状态代码和错误页面或错误消息。
我目前正常控制器的设置:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
@Controller
public class FooController {
@RequestMapping("/foo") public String foo() {
try {
...
} catch (EntityNotFoundException enfe) {
throw new ResourceNotFoundException(enfe.getMessage());
}
return "foo";
}
}
对于REST cotrollers,我不会捕获异常并让全局异常处理程序接收它:
@Controller
public class BarController {
@RequestMapping("/bar")
@ResponseBody
public SomeDto bar() throws EntityNotFoundException {
...
return someDto;
}
}
@ControllerAdvice
public class ExceptionHandlerAdvice {
@ExceptionHandler(EntityNotFoundException.class)
public final ResponseEntity<Object> handleEntityNotFoundExceptionEntityNotFoundException enfe) {
return new ResponseEntity<>(enfe.getMessage, HttpStatus.NOT_FOUND);
}
}
我也不想抓住并重新考虑我的普通控制器。全局处理程序应该同时处理:
@ExceptionHandler(EntityNotFoundException.class)
public final Object handleEntityNotFoundException(EntityNotFoundException enfe) {
if (/* is REST controller? */) {
return new ResponseEntity<>(enfe.getMessage(), HttpStatus.NOT_FOUND);
} else {
Map<String, Object> model = ImmutableMap.of("message", enfe.getMessage());
return new ModelAndView("error", model, HttpStatus.NOT_FOUND);
}
}
有没有办法确定异常的来源,即控制器是否注明@ResponseBody
类似的东西?
答案 0 :(得分:3)
我找到了解决方案。您可以将当前控制器方法注入HandlerMethod
参数。
@ExceptionHandler(EntityNotFoundException.class)
public final Object handleEntityNotFoundException(EntityNotFoundException enfe, HandlerMethod handlerMethod) {
boolean isRestController = handlerMethod.hasMethodAnnotation(ResponseBody.class)
|| handlerMethod.getBeanType().isAnnotationPresent(ResponseBody.class)
|| handlerMethod.getBeanType().isAnnotationPresent(RestController.class);
if (isRestController) {
return new ResponseEntity<>(enfe.getMessage(), HttpStatus.NOT_FOUND);
} else {
Map<String, Object> model = ImmutableMap.of("message", enfe.getMessage());
return new ModelAndView("error", model, HttpStatus.NOT_FOUND);
}
}
答案 1 :(得分:0)
您可以将错误代码用作源reasone / point,或者为从ResourceNotFoundException扩展的不同异常源创建separeate异常。
使用错误代码,您使用一个例外并使用不同的代码,除了例外,您应该创建exrta类。
public class ResourceNotFoundException extends RuntimeException {
private Long sourceErrorCode;//if you use java8 use Optional
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message , Long sourceErrorCode) {
super(message);
this.sourceErrorCode = sourceErrorCode;
}
}
抛出
......
catch (EntityNotFoundException enfe) {
throw new ResourceNotFoundException(enfe.getMessage() , ERROR_SOURCE_CODE);
}
和Handler
@ExceptionHandler(EntityNotFoundException.class)
private Set<Long> REST_CODES = ..add... {code1 , code2...}
public final Object handleSlugNotFoundException(EntityNotFoundException enfe) {
//if you use java 8 use Optional
if (REST_CODES.contains(enfe.getErrorCode())/* is REST controller? */) {
return new ResponseEntity<>(enfe.getMessage(), HttpStatus.NOT_FOUND);
} else {
Map<String, Object> model = ImmutableMap.of("message", enfe.getMessage());
return new ModelAndView("error", model, HttpStatus.NOT_FOUND);
}
}
并为错误代码创建类持有者或枚举以避免代码重复
答案 2 :(得分:0)
我会对普通控制器和REST控制器使用单独的异常,而不是使用特殊的返回代码或消息做一些魔术。这样您就可以编写简单的目标异常处理程序。
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class RestResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}