Джерси... как регистрировать все исключения, но при этом вызывать ExceptionMappers
Я немного привязан... хочу свой торт и съесть его тоже.
Я хочу регистрировать все исключения, которые выбрасывает мое приложение. Поэтому, если кто-то нажимает неверный URL, я хочу записать трассировку стека в SLF4J.
Таким образом, вы, вероятно, думаете: "Эй, это просто, просто внедрите карту исключений и зарегистрируйте исключение". Итак, я сделал:
public class RestExceptionMapper implements ExceptionMapper<java.lang.Exception> {
private static final Logger log = LoggerFactory.getLogger(RestExceptionMapper.class);
/**
* {@inheritDoc}
*/
@Override
public Response toResponse(Exception exception) {
log.error("toResponse() caught exception", exception);
return null;
}
}
Если вы сделаете это, вместо 404 ошибок, когда кто-то вводит неправильный URL, он получит 500 ошибок. Можно было бы предположить, что возвращение null распространит исключение по цепочке обработчиков, но Джерси этого не делает. На самом деле он предоставляет очень мало информации, почему он выбрал бы один обработчик над другим...
Кто-нибудь сталкивался с этой проблемой и как вы ее решили?
2 ответа
Чтобы вернуть правильный код статуса http, ваш преобразователь исключений может выглядеть примерно так:
@Provider
public class RestExceptionMapper implements ExceptionMapper<Throwable>
{
private static final Logger log = LoggerFactory.getLogger(RestExceptionMapper.class);
@Override
public Response toResponse(Throwable exception)
{
log.error("toResponse() caught exception", exception);
return Response.status(getStatusCode(exception))
.entity(getEntity(exception))
.build();
}
/*
* Get appropriate HTTP status code for an exception.
*/
private int getStatusCode(Throwable exception)
{
if (exception instanceof WebApplicationException)
{
return ((WebApplicationException)exception).getResponse().getStatus();
}
return Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
}
/*
* Get response body for an exception.
*/
private Object getEntity(Throwable exception)
{
// return stack trace for debugging (probably don't want this in prod...)
StringWriter errorMsg = new StringWriter();
exception.printStackTrace(new PrintWriter(errorMsg));
return errorMsg.toString();
}
}
Также кажется, что вы заинтересованы в каскадных преобразователях исключений, но в соответствии со спецификацией это невозможно:
Спецификация JAX-RS 2.0, глава 4.4
"Поставщики сопоставления исключений отображают проверенное исключение или исключение во время выполнения на экземпляр Response. Поставщик сопоставления исключений реализует интерфейс ExceptionMapper и может быть аннотирован @Provider для автоматического обнаружения. При выборе поставщика сопоставления исключений для сопоставления исключения, реализация ДОЛЖНА использовать реализацию. поставщик, чей универсальный тип является ближайшим суперклассом исключения.
Когда класс ресурса или метод поставщика генерирует исключение, для которого существует поставщик сопоставления исключений, соответствующий поставщик используется для получения экземпляра Response. Полученный ответ обрабатывается так, как если бы метод веб-ресурса возвратил ответ, см. Раздел 3.3.3. В частности, отображенный Ответ ДОЛЖЕН быть обработан с использованием цепочки фильтров ContainerResponse, определенной в Главе 6.
Чтобы избежать потенциально бесконечного цикла, во время обработки запроса и его соответствующего ответа необходимо использовать одно сопоставление исключений. Реализации JAX-RS НЕ ДОЛЖНЫ пытаться отобразить исключения, возникшие при обработке ответа, ранее отображенного из исключения. Вместо этого это исключение ДОЛЖНО обрабатываться, как описано в шагах 3 и 4 в разделе 3.3.4."
Вы можете использовать RequestEventListener, чтобы прослушивать событие исключения и регистрировать бросаемый объект, не мешая какой-либо существующей обработке. Обратите внимание, что это означает, что сначала нужно зарегистрировать ApplicationEventListener
который затем возвращает экземпляр RequestEventListener
,
public class ExceptionLogger implements ApplicationEventListener, RequestEventListener {
private static final Logger log = LoggerFactory.getLogger(RequestExceptionLogger.class);
@Override
public void onEvent(final ApplicationEvent applicationEvent) {
}
@Override
public RequestEventListener onRequest(final RequestEvent requestEvent) {
return this;
}
@Override
public void onEvent(RequestEvent paramRequestEvent) {
if(paramRequestEvent.getType() == Type.ON_EXCEPTION) {
log.error("", paramRequestEvent.getException());
}
}
}