TL; DR
Легкого выхода нет. Если бы я был тобой, я бы использовал общие страницы ошибок c. Вы можете настроить их для различных типов ошибок.
Инструменты, не отвечающие вашей проблеме.
IMO Описанная вами проблема лучше подходит для SPA (одностраничного приложения), написанного на любом современном каркас внешнего интерфейса с java бэкэндом, предоставляющим REST API. Обычно enpoint возвращает json, содержащий фактические запрошенные данные. Но в случае любой ошибки вы можете просто вернуть код 4xx с описанием ошибки в качестве полезной нагрузки. Тогда логи презентации c в javascript могут отображать все что угодно без перезагрузки страницы.
Поскольку вы решили использовать тимилиф, который является движком шаблонов, вы должны принять его ограничения. Большинство вызовов к контроллеру - это запрос на просмотр. Spring and Thymeleaf берет файл шаблона, заполняет его данными модели и отправляет обратно всю страницу в ваш браузер. Если этот процесс завершится неудачно во время сбора данных, страница не будет правильно отображена. Поэтому самое разумное, что нужно сделать - перенаправить на страницу с ошибкой.
Разумный выход
Если страницы ошибок generi c отпугивают вас, можно предпринять дополнительные усилия. Вместо общей обработки ошибок c вы можете обрабатывать исключения в каждом методе контроллера. В случае ошибки вы можете указать другую модель с ошибками, которая будет использоваться для заполнения шаблона. Это дает вам гибкость в том, как отображается ошибка. Это будет зависеть от того, как вы подготовили шаблон.
Например:
Просмотр. html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>Data:</h1>
<div th:if="${actualData}">
<p th:text="${actualData}"></p>
</div>
<!-- Instead of div below, you could supply a modal and pice of javascript opening it -->
<div th:if="${errorData}">
<h3>Error occurred:</h3>
<p th:text="${errorData}"></p>
</div>
</body>
</html>
MyController. java
@Controller
class MyController {
@GetMapping("/view")
public String view(Model model) {
try {
model.addAttribute("actualData", mayThrowExceptionDataFetch());
} catch(Exception e) {
model.addAttribute("errorData", e.getMessage());
}
return "view";
}
private String mayThrowExceptionDataFetch() {
// Either return a value or throw an exception
}
}
Таким образом, представление всегда будет возвращаться. Даже при возникновении ошибки она обрабатывается и отображается.
Необоснованный выход
Важное примечание: Это только демонстрационная версия. Конечно, я бы не попробовал это в работе.
Я нашел ваши требования настолько необычными (исключения контроллера, как триггер для открытия модальной страницы), что я хотел проверить, возможно ли что-то подобное. Я предположил, что решение может быть что-то вроде:
- Интерфейс: подписаться на конечную точку событий отправки сервера (источник ошибок).
- Бэкэнд: при перехвате исключения отменить запрос так что сервер ничего не возвращает.
- Backend: отправьте сообщение, содержащее описание ошибки, клиенту-зависимому.
- Frontend: получите событие, предупредите его как-нибудь.
Затем я создаю эту рабочую демонстрацию, которая действительно не вызывает перезагрузки страницы, но отображает ошибку:
index. html
<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
</head>
<body>
<a th:href="@{/exception}">Click for exception</a>
<script>
var sse = new EventSource('http://localhost:8080/sse');
sse.onmessage = function (evt) {
alert(evt.data);
};
</script>
</body>
</html>
MyService. java
@Service
public class MyService {
private final SseEmitter emitter = new SseEmitter();
public SseEmitter getEmitter() {
return emitter;
}
}
MyExceptionHandler. java
@ControllerAdvice
public class MyExceptionHandler {
private final MyService service;
@Autowired
public MyExceptionHandler(MyService service) {
this.service = service;
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.CONTINUE) // I don't figure out how to abort request
public ResponseEntity<Void> handle(Exception ex) {
try {
service.getEmitter().send(SseEmitter.event().data(ex.getMessage()));
} catch (IOException e) {
// TODO
}
}
}
MyController. java
@Controller
public class MyController {
private final MyService service;
@Autowired
public MyCtrl(MyService service) {
this.service = service;
}
@GetMapping("/sse")
public SseEmitter sse() {
return service.getEmitter();
}
@GetMapping("/index")
public String index() {
return "index";
}
@GetMapping("/exception")
public String exception() {
throw new RuntimeException("Oops :o");
}
}
Все вышеперечисленное - это проект весенней загрузки с зависимостями spring-boot-starter-thymeleaf
и spring-boot-starter-web
. Запустите приложение, go до localhost:8080/index
, затем нажмите на ссылку. Предупреждение должно появляться без перезагрузки страницы.