У меня есть приложение Spring 4 с ControllerAdvice, расширяющим ResponseEntityExceptionHandler, которое отлично работает при возникновении исключения.
@ControllerAdvice
@Slf4j
public class ExceptionManager extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
final ValidationErrors errors = getErrors(ex.getBindingResult());
headers.setContentType(MediaType.APPLICATION_JSON);
return handleExceptionInternal(ex, errors, headers, HttpStatus.BAD_REQUEST, request);
}
@Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
final ValidationErrors errors = getErrors(ex.getBindingResult());
headers.setContentType(MediaType.APPLICATION_JSON);
return handleExceptionInternal(ex, errors, headers, HttpStatus.BAD_REQUEST, request);
}
@ExceptionHandler(ValidationException.class)
public ResponseEntity handleValidationException(ValidationException ex) {
log.warn("Validation error", ex);
return ex.toResponseEntity();
}
@ExceptionHandler(HttpClientErrorException.class)
public ResponseEntity handleHttpClientErrorException(HttpClientErrorException ex) {
log.error(ex.getMessage());
return new ResponseEntity(ex.getResponseBodyAsString(), ex.getStatusCode());
}
@ExceptionHandler({Exception.class, RuntimeException.class})
protected ResponseEntity<Object> handleRootException(Exception ex, WebRequest request) {
return handleException(request, ex);
}
...
}
Однако в случае исключения unmarshaller, например
Exception received:
org.springframework.http.converter.HttpMessageNotReadableException: Could not unmarshal to [class com.company.etc.v1.PojoResponse]: null; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 5; columnNumber: 28; Attribute name "entResponse" associated with an element type "assetDocum" must be followed by the ' = ' character.]
at org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter.readFromSource(Jaxb2RootElementHttpMessageConverter.java:149)
at org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter.readInternal(AbstractXmlHttpMessageConverter.java:61)
at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:104)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:917)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:901)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531)
at com.company.service.impl.SearchAsset.searchAssets(SearchAsset.java:42)
... 57 common frames omitted
Caused by: javax.xml.bind.UnmarshalException: null
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.createUnmarshalException(AbstractUnmarshallerImpl.java:335)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.createUnmarshalException(UnmarshallerImpl.java:563)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:249)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:214)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:140)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:123)
at org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter.readFromSource(Jaxb2RootElementHttpMessageConverter.java:133)
... 127 common frames omitted
Caused by: org.xml.sax.SAXParseException: Attribute name "entResponse" associated with an element type "assetDocum" must be followed by the ' = ' character.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177)
... 131 common frames omitted
исключение не поймано! Это происходит в конкретном случае, когда @RestController использует службу, которая вызывает внешнюю конечную точку. В моем случае внешняя служба возвращает XML, и если он поврежден (как в исключении выше), то SAXParseException
не перехватывается и автоматически преобразуется весной как 400 Bad Request с пустым телом. Он также вообще не регистрируется.
Если в моем RestController я добавлю метод @ExceptionHandler(Throwable.class)
, он вызывается, и я могу контролировать отсылаемую ошибку.
Это нормально? Я думал, что ControllerAdvice может централизовать всю обработку исключений, избавляя от необходимости иметь ExceptionHandler в каждом контроллере.
handleRootExceptioninside
передается Throwable, а не Exception ex. - person Vinay Prajapati   schedule 26.10.2017@ExceptionHandler(Throwable.class) @ResponseBody public ResponseEntity handleRootException(Throwable ex, WebRequest req) { log.error("Exception received: ", ex); return new ResponseEntity(ex, HttpStatus.INTERNAL_SERVER_ERROR); }
, но до сих пор не вызывается, поэтому этот точный код работает, если его поместить в контроллер. - person petronius   schedule 26.10.2017@ExceptionHandler
в контроллере. К вашему сведению, ответ XML прерывается возвратом строки в середине тега, чтобы спровоцировать исключение. - person petronius   schedule 26.10.2017HttpMessageNotReadableException
; если вы не обрабатываете это специально, следует вызвать общий метод handleRootException(). Этого не происходит? - person Gimby   schedule 26.10.2017