Обработчики исключений
Необработанные исключения, выброшенные в потоке HTTP-запроса, передаются механизму обработки исключений Jmix. Он содержит список обработчиков, каждый из которых может либо обработать исключение, либо отказаться. Если исключение не обработано ни одним из обработчиков, оно передается в DefaultUiExceptionHandler
, который отображает диалог с сообщением об исключении и стек вызовов. Вы можете настроить этот диалог, как показано ниже.
Обработчики исключений приложения
Вы можете предоставить собственные обработчики для любых исключений. Самый простой способ сделать это - создать подкласс AbstractUiExceptionHandler
и зарегистрировать его как бин Spring.
Например, если у вас есть следующее исключение:
package com.company.onboarding.exception;
public class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
}
Вы можете определить для него обработчик следующим образом:
package com.company.onboarding.exception;
import io.jmix.flowui.Notifications;
import io.jmix.flowui.exception.AbstractUiExceptionHandler;
import org.springframework.stereotype.Component;
@Component (1)
public class MyExceptionHandler extends AbstractUiExceptionHandler { (2)
private final Notifications notifications; (3)
public MyExceptionHandler(Notifications notifications) {
super(MyException.class.getName()); (4)
this.notifications = notifications;
}
@Override
protected void doHandle(String className, String message, Throwable throwable) {
notifications.show("My exception", throwable.getMessage()); (5)
}
}
1 | - Сделайте обработчик бином Spring. |
2 | - Расширьте AbstractUiExceptionHandler . |
3 | - Инжектируйте любые другие бины Spring, если необходимо. |
4 | - Передайте полное имя класса исключения в конструктор суперкласса. |
5 | - Обработайте исключение в методе doHandle() . |
Ваш обработчик исключений будет автоматически добавлен в список обработчиков фреймворка. Позицию вашего обработчика в списке можно настроить, добавив аннотацию @Order
к бину. Например, если установить порядок как @Order(JmixOrder.HIGHEST_PRECEDENCE - 10)
, ваш обработчик получит приоритет перед обработчиком фреймворка для того же исключения.
Если необходимо больше контроля над тем, какие исключения должны обрабатываться вашим обработчиком, либо переопределите метод canHandle()
базового класса AbstractUiExceptionHandler
, либо вовсе не используйте AbstractUiExceptionHandler
и реализуйте интерфейс UiExceptionHandler
напрямую. См. также документацию Vaadin по обработке исключений UI на более низком уровне.
Обработчик нарушений уникальности
Jmix предоставляет встроенный обработчик нарушений ограничений уникальности базы данных: UniqueConstraintViolationHandler
. Его можно легко настроить в двух аспектах.
Во-первых, вы можете изменить сообщение, которое отображается пользователю в ответ на ошибку. Сообщение устанавливается в пакете сообщений с ключом в формате databaseUniqueConstraintViolation.<DB_CONSTRAINT_NAME>
, например:
databaseUniqueConstraintViolation.IDX_DEPARTMENT_UNQ_NAME=Отдел с таким именем уже существует
Во-вторых, вы можете предоставить свой собственный шаблон для распознавания нарушений уникальности. Фреймворк содержит шаблоны по умолчанию для каждого типа базы данных, например, для PostgreSQL это ERROR: duplicate key value violates unique constraint "(.+)"
. Вы можете найти шаблоны по умолчанию в реализациях интерфейса DbmsFeatures
. Если шаблон по умолчанию не работает в ваших условиях (например из-за локализации базы данных), предоставьте свой шаблон в свойстве jmix.data.unique-constraint-violation-pattern.
Настройка обработчика по умолчанию
Вы можете настроить диалог, отображаемый обработчиком исключений по умолчанию. В приведенном ниже примере демонстрируется, как добавить кнопку, позволяющую пользователям сообщать об ошибке системному администратору.
Во-первых, создайте свой класс диалога на базе ExceptionDialog
и переопределите соответствующие методы:
package com.company.onboarding.exception;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import io.jmix.flowui.exception.ExceptionDialog;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE) (1)
public class MyExceptionDialog extends ExceptionDialog { (2)
public MyExceptionDialog(Throwable throwable) {
super(throwable);
}
@Override
protected HorizontalLayout createButtonsPanel() { (3)
HorizontalLayout buttonsPanel = super.createButtonsPanel();
Button button = uiComponents.create(Button.class);
button.setText("Report to admin");
button.addClickListener(e -> {
// ...
});
buttonsPanel.add(button);
return buttonsPanel;
}
}
1 | - Зарегистрируйте класс как бин-прототип. |
2 | - Расширьте ExceptionDialog . |
3 | - Переопределите соответствующие методы для настройки диалога. |
Затем создайте класс-провайдер:
package com.company.onboarding.exception;
import io.jmix.flowui.exception.ExceptionDialog;
import io.jmix.flowui.exception.ExceptionDialogProvider;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Component;
@Component
public class MyExceptionDialogProvider implements ExceptionDialogProvider {
private final ObjectProvider<MyExceptionDialog> myExceptionDialogProvider; (1)
public MyExceptionDialogProvider(ObjectProvider<MyExceptionDialog> myExceptionDialogProvider) {
this.myExceptionDialogProvider = myExceptionDialogProvider;
}
@Override
public boolean supports(Throwable throwable) {
return true; (2)
}
@Override
public ExceptionDialog getExceptionDialogOpener(Throwable throwable) {
return myExceptionDialogProvider.getObject(throwable); (3)
}
}
1 | - Используйте ObjectProvider , потому что MyExceptionDialog является бином-прототипом. |
2 | - Провайдер может вступать в действие только для конкретных исключений. Верните true , если вы хотите, чтобы он работал для всех типов исключений. |
3 | - Верните новый экземпляр MyExceptionDialog . |
Фреймворк самостоятельно подхватит вашего провайдера и будет использовать возвращенный диалог для необработанных исключений.