Я считаю, что все исключения, которые не возникают во время маршрутизации, будут переданы ErrorHandler VaadinSession, в котором произошла ошибка.
Лучший способ установить ErrorHandler - переопределить метод sessionInit
в пользовательском SessionInitListener
Вы можете добавить пользовательский SessionInitListener
в методе servletInitialized
пользовательского VaadinServlet.
class CustomServlet extends VaadinServlet{
@Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(new CustomSessionInitListener());
}
}
И SessionInitListener
(в этом примере CustomSessionInitListener
) должен установить errorHandler для сеансов, которые инициализируются.
class CustomSessionInitListener implements SessionInitListener{
@Override
public void sessionInit(SessionInitEvent event) throws ServiceException {
event.getSession().setErrorHandler(new CustomErrorHandler());
}
}
Для получения дополнительной информации о том, как создать свой собственный сервлет, обратитесь к Страница учебного пособия Vaadin (вам нужно прокрутить вниз до "Настройка сервлета Vaadin")
Edit:
Чтобы показать страницу с ошибкой, вам нужно, чтобы Vaadin перенаправил сообщение об ошибке. Чтобы добиться этого, мы можем использовать BeforeEnterEvent
, BeforeEnterEvents
и метод rerouteToError
, который мы можем использовать, чтобы Vaadin показал наш ErrorView.
Но мы также хотим передать экземпляр Exception, поэтому мы должны также сохранить его. Я сделал именно это со следующим классом:
@Route("error-view") // Route shown in the user's browser
public class ErrorViewShower extends Div implements BeforeEnterObserver {
// Class to store the current Exception of each UI in
private static class UIExceptionContainer extends HashMap<UI, Exception> {
}
// Method to call when we want to show an error
public static void showError(Exception exception) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Creating and setting the exceptionContainer in case it hasn't been set yet.
if (exceptionContainer == null) {
exceptionContainer = new UIExceptionContainer();
VaadinSession.getCurrent().setAttribute(UIExceptionContainer.class, exceptionContainer);
}
// Storing the exception for the beforeEnter method
exceptionContainer.put(UI.getCurrent(), exception);
// Now we navigate to an Instance of this class, to use the BeforeEnterEvent to reroute to the actual error view
UI.getCurrent().navigate(ErrorViewShower.class);// If this call doesn't work you might want to wrap into UI.access
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Retrieving the previously stored exception. You might want to handle if this has been called without setting any Exception.
Exception exception = exceptionContainer.get(UI.getCurrent());
//Clearing out the now handled Exception
exceptionContainer.remove(UI.getCurrent());
// Using the BeforeEnterEvent to show the error
event.rerouteToError(exception, "Possible custom message for the ErrorHandler here");
}
}
Использование его в сочетании с обработчиком ошибок выглядит следующим образом:
public class CustomErrorHandler implements ErrorHandler {
@Override
public void error(ErrorEvent event) {
// This can easily throw an exception itself, you need to add additional checking before casting.
// And it's possible that this method is called outside the context of an UI(when a dynamic resource throws an exception for example)
Exception exception = (Exception) event.getThrowable();
ErrorViewShower.showError(exception);
}
}
Edit2:
Оказывается, что исключения, возникающие во внутренних вызовах методов, не обрабатываются ErrorHandler пользовательского интерфейса или ErrorHandler VaadinSession, а вместо этого другим обработчиком ошибок, который приводит к завершению клиентской стороны и отображает уведомление об ошибке,
решение состоит в том, чтобы перехватывать исключения в методах DataProvider и передавать их в ErrorViewShower.showError()
и возвращать без каких-либо исключений, направляя стековую трассу вверх. (Или не создавайте исключение самостоятельно, а просто передайте новый метод ErrorViewShower.showError()
).
Возвращаясь нормально, Ваадин даже не знает, что что-то пошло не так.
ErrorViewShower.showError()
вызывает ui.navigate
, эта команда навигации, похоже, «ставится в очередь» за вызовами к DataProvider, что означает, что представление пользователя изменится в том же запросе.
Поставщик данных с такой реализацией:
new AbstractBackEndDataProvider<String, Void>() {
@Override
protected Stream<String> fetchFromBackEnd(Query<String, Void> query) {
try{
//Code that can throw an Exception here
}catch(Exception e){
ErrorViewShower.showError(e);
//We have to make sure that query.getLimit and query.getOffset gets called, otherwise Vaadin throws an Exception with the message "the data provider hasn't ever called getLimit() method on the provided query. It means that the the data provider breaks the contract and the returned stream contains unxpected data."
query.getLimit();
query.getOffset();
return Stream.of(); //Stream of empty Array to return without error
}
}
@Override
protected int sizeInBackEnd(Query<String, Void> query) {
//Second way i mentioned, but this will not catch any Exception you didn't create, where as the try...catch has no way to let any Exception reach Vaadin.
if(badThingsHappened){
ErrorViewShower.showError(new UnsupportedOperationException("Bad things..."));
return 0;//Exiting without error
}
}
}