Próbuję skonfigurować serwer usług REST na dużą skalę. Używamy Spring Boot 1.2.1 Spring 4.1.5 i Java 8. Nasze kontrolery implementują @RestController i standardowe adnotacje @RequestMapping.
Mój problem polega na tym, że Spring Boot ustawia domyślne przekierowanie dla wyjątków kontrolera do /error
. Z dokumentów:
Spring Boot domyślnie udostępnia mapowanie błędów /, które obsługuje wszystkie błędy w rozsądny sposób i jest rejestrowane jako „globalna” strona błędu w kontenerze serwletu.
Od lat pisząc aplikacje REST w Node.js, nie jest to dla mnie rozsądne. Każdy wyjątek generowany przez punkt końcowy usługi powinien zostać zwrócony w odpowiedzi. Nie rozumiem, dlaczego wysyłasz przekierowanie do konsumenta, który najprawdopodobniej jest konsumentem Angular lub JQuery SPA, który szuka tylko odpowiedzi i nie może lub nie może podjąć żadnych działań dotyczących przekierowania.
To, co chcę zrobić, to skonfigurować globalną procedurę obsługi błędów, która może przyjąć każdy wyjątek - celowo wyrzuconą z metody mapowania żądań lub automatycznie wygenerowaną przez Spring (404, jeśli żadna metoda obsługi nie zostanie znaleziona dla podpisu ścieżki żądania) i zwróci standardowa sformatowana odpowiedź na błąd (400, 500, 503, 404) do klienta bez żadnych przekierowań MVC. W szczególności weźmiemy błąd, zapiszemy go do NoSQL z UUID, a następnie zwrócimy klientowi właściwy kod błędu HTTP z UUID wpisu dziennika w treści JSON.
Dokumentacja nie wyjaśnia, jak to zrobić. Wydaje mi się, że musisz albo stworzyć własną implementację ErrorController, albo w jakiś sposób użyć ControllerAdvice , ale wszystkie przykłady, które widziałem, nadal obejmują przekazywanie odpowiedzi na jakieś mapowanie błędów, co nie pomaga. Inne przykłady sugerują, że musisz wymienić wszystkie typy wyjątków, które chcesz obsłużyć, zamiast po prostu wymieniać „Throwable” i pobierać wszystko.
Czy ktoś może mi powiedzieć, co przegapiłem, lub wskazać właściwy kierunek, jak to zrobić, bez sugerowania łańcucha, z którym Node.js byłby łatwiejszy do rozwiązania?
źródło
Odpowiedzi:
Nowa odpowiedź (2016-04-20)
Używanie Spring Boot 1.3.1.RELEASE
Nowy krok 1 - dodanie następujących właściwości do application.properties jest łatwe i mniej uciążliwe:
Jeśli pracujesz z pełną aplikacją RESTful, bardzo ważne jest, aby wyłączyć automatyczne mapowanie zasobów statycznych, ponieważ jeśli używasz domyślnej konfiguracji Spring Boot do obsługi zasobów statycznych, to program obsługi zasobów będzie obsługiwał żądanie (jest zamówione jako ostatnie i zmapowane do / ** co oznacza, że przejmuje wszystkie żądania, które nie zostały obsłużone przez żadną inną procedurę obsługi w aplikacji), więc aplet dyspozytora nie ma szans na rzucenie wyjątku.
Nowa odpowiedź (04.12.2015)
Używanie Spring Boot 1.2.7.RELEASE
Nowy krok 1 - znalazłem znacznie mniej inwazyjny sposób ustawiania flagi „throExceptionIfNoHandlerFound”. Zastąp poniższy kod zastępczy DispatcherServlet (krok 1) tym w klasie inicjalizacji aplikacji:
W tym przypadku ustawiamy flagę na istniejącym DispatcherServlet, który zachowuje automatyczną konfigurację przez framework Spring Boot.
Jeszcze jedna rzecz, którą znalazłem - adnotacja @EnableWebMvc jest zabójcza dla Spring Boot. Tak, ta adnotacja umożliwia takie rzeczy, jak wychwycenie wszystkich wyjątków kontrolera, jak opisano poniżej, ale także zabija DUŻO pomocnej automatycznej konfiguracji, którą normalnie zapewnia Spring Boot. Używaj tej adnotacji z najwyższą ostrożnością podczas korzystania z Spring Boot.
Oryginalna odpowiedź:
Po wielu dalszych badaniach i śledzeniu rozwiązań zamieszczonych tutaj (dzięki za pomoc!) I niemałej ilości śledzenia środowiska wykonawczego w kodzie Spring, w końcu znalazłem konfigurację, która będzie obsługiwać wszystkie wyjątki (nie błędy, ale czytaj dalej) w tym 404s.
Krok 1 - powiedz SpringBoot, aby przestał używać MVC w sytuacjach „nie znaleziono obsługi”. Chcemy, aby Spring zgłosił wyjątek zamiast zwracać klientowi przekierowanie widoku do „/ error”. Aby to zrobić, musisz mieć wpis w jednej z klas konfiguracji:
Wadą tego jest to, że zastępuje domyślny serwlet dyspozytora. Nie był to jeszcze dla nas problem, bez żadnych skutków ubocznych ani problemów z wykonywaniem. Jeśli zamierzasz zrobić cokolwiek innego z serwletem dyspozytorskim z innych powodów, to jest to miejsce, w którym możesz to zrobić.
Krok 2 - Teraz, gdy rozruch sprężynowy wyrzuci wyjątek, gdy nie zostanie znaleziony żaden program obsługi, ten wyjątek może być obsłużony z każdym innym w ujednoliconej obsłudze wyjątków:
Pamiętaj, że uważam, że adnotacja „@EnableWebMvc” jest tutaj znacząca. Wydaje się, że bez tego nic z tego nie działa. I to wszystko - Twoja aplikacja rozruchowa Spring będzie teraz przechwytywać wszystkie wyjątki, w tym 404, w powyższej klasie obsługi i możesz z nimi robić, co chcesz.
Ostatnia kwestia - wydaje się, że nie ma sposobu, aby to złapać wyrzucone błędy. Mam szalony pomysł wykorzystania aspektów do wychwytywania błędów i przekształcania ich w wyjątki, z którymi może sobie poradzić powyższy kod, ale nie miałem jeszcze czasu, aby faktycznie spróbować to zaimplementować. Mam nadzieję, że to komuś pomoże.
Wszelkie komentarze / poprawki / ulepszenia będą mile widziane.
źródło
@ExceptionHandler
metody wywoływanej podczas umieszczania jej w@ControllerAdvice
klasie, chociaż działają poprawnie, jeśli zostaną umieszczone w@RestController
klasie.@EnableWebMvc
znajduje się w klasie@ControllerAdvice
i@Configuration
(testowałem każdą kombinację). Każdy pomysł lub działający przykład? // @Andy WilkinsonWraz z Spring Boot 1.4+ dodano nowe fajne klasy dla łatwiejszej obsługi wyjątków, które pomagają w usunięciu standardowego kodu.
Nowy
@RestControllerAdvice
Udostępniono obsługę wyjątków, jest to kombinacja@ControllerAdvice
i@ResponseBody
. Można usunąć@ResponseBody
na@ExceptionHandler
metodzie kiedy korzystać z tej nowej adnotacji.to znaczy
Do obsługi błędów 404
@EnableWebMvc
wystarczyło dodanie adnotacji i następujących do application.properties:spring.mvc.throw-exception-if-no-handler-found=true
Możesz znaleźć i bawić się źródłami tutaj:
https://github.com/magiccrafter/spring-boot-exception-handling
źródło
@RestControllerAdvice
bez dodatkowej konfiguracji. Czego tu brakuje?Myślę, że
ResponseEntityExceptionHandler
spełnia Twoje wymagania. Przykładowy fragment kodu dla HTTP 400:Możesz sprawdzić ten post
źródło
HttpRequestMethodNotSupportedException
i podłączać ten sam plik jar do wielu mikroserwisów, w niektórych celach biznesowych musimy odpowiedzieć na nazwę aliasu mikroserwisu w odpowiedzi. czy jest jakiś sposób, abyśmy mogli uzyskać podstawową nazwę mikrousługi / nazwę kontrolera? Wiem, że podamHandlerMethod
nazwę metody java, z której pochodzi wyjątek. Ale tutaj żadna z metod nie otrzymała żądania, dlategoHandlerMethod
nie zostanie zainicjowana. Czy jest więc jakieś rozwiązanie tego problemu?Chociaż jest to starsze pytanie, chciałbym podzielić się swoimi przemyśleniami na ten temat. Mam nadzieję, że niektórym z Was będzie to pomocne.
Obecnie buduję REST API, które wykorzystuje Spring Boot 1.5.2.RELEASE z Spring Framework 4.3.7.RELEASE. Używam podejścia Java Config (w przeciwieństwie do konfiguracji XML). Ponadto mój projekt wykorzystuje globalny mechanizm obsługi wyjątków przy użyciu
@RestControllerAdvice
adnotacji (patrz poniżej).Mój projekt ma takie same wymagania jak Twój: chcę, aby mój REST API zwracał a
HTTP 404 Not Found
wraz z towarzyszącym ładunkiem JSON w odpowiedzi HTTP do klienta API, gdy próbuje wysłać żądanie na adres URL, który nie istnieje. W moim przypadku ładunek JSON wygląda następująco (przy okazji wyraźnie różni się od domyślnego Spring Boot):W końcu udało mi się. Oto w skrócie główne zadania, które musisz wykonać:
NoHandlerFoundException
jest generowany, jeśli klienci API wywołują adresy URL, dla których nie istnieje metoda obsługi (zobacz krok 1 poniżej).ApiError
), która zawiera wszystkie dane, które powinny zostać zwrócone do klienta API (patrz krok 2).NoHandlerFoundException
i zwraca odpowiedni komunikat o błędzie do klienta API (patrz krok 3).Ok, teraz do szczegółów:
Krok 1: Skonfiguruj application.properties
Musiałem dodać następujące dwa ustawienia konfiguracyjne do
application.properties
pliku projektu :Zapewnia to, że
NoHandlerFoundException
jest generowany w przypadkach, gdy klient próbuje uzyskać dostęp do adresu URL, dla którego nie istnieje metoda kontrolera, która byłaby w stanie obsłużyć żądanie.Krok 2: Utwórz klasę dla błędów interfejsu API
Zrobiłem zajęcia podobne do sugerowanych w tym artykule na blogu Eugena Paraschiva. Ta klasa reprezentuje błąd interfejsu API. Ta informacja jest wysyłana do klienta w treści odpowiedzi HTTP w przypadku błędu.
Krok 3: Utwórz / skonfiguruj globalną obsługę wyjątków
Do obsługi wyjątków używam następującej klasy (dla uproszczenia usunąłem instrukcje importu, kod logowania i kilka innych, nieistotnych fragmentów kodu):
Krok 4: Napisz test
Chcę się upewnić, że API zawsze zwraca poprawne komunikaty o błędach do klienta wywołującego, nawet w przypadku niepowodzenia. Dlatego napisałem taki test:
@ActiveProfiles("dev")
Adnotacja może pozostać z dala. Używam go tylko wtedy, gdy pracuję z różnymi profilami.RegexMatcher
Jest zwyczaj Hamcrest dopasowujący używać do lepszych pól datownika uchwytem. Oto kod (znalazłem go tutaj ):Kilka dalszych uwag z mojej strony:
@EnableWebMvc
adnotacji. W moim przypadku nie było to konieczne.źródło
A co z tym kodem? Używam mapowania żądań rezerwowych, aby wychwycić błędy 404.
źródło
Domyślnie Spring Boot wyświetla json ze szczegółami błędów.
Działa również w przypadku wszelkiego rodzaju błędów mapowania żądań. Sprawdź ten artykuł http://www.jayway.com/2014/10/19/spring-boot-error-responses/
Jeśli chcesz utworzyć, zaloguj się do NoSQL. Możesz utworzyć @ControllerAdvice, w którym chcesz to zarejestrować, a następnie ponownie zgłosić wyjątek. W dokumentacji jest przykład https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
źródło
@RestControllerAdvice to nowa funkcja Spring Framework 4.3 do obsługi wyjątków z RestfulApi poprzez rozwiązanie problemu:
źródło
W przypadku kontrolerów REST polecałbym użyć
Zalando Problem Spring Web
.https://github.com/zalando/problem-spring-web
Jeśli Spring Boot ma na celu osadzenie automatycznej konfiguracji, ta biblioteka robi więcej w zakresie obsługi wyjątków. Wystarczy dodać zależność:
Następnie zdefiniuj jedną lub więcej cech porad dla wyjątków (lub użyj tych, które są domyślnie)
Następnie możesz zdefiniować porady kontrolera dotyczące obsługi wyjątków jako:
źródło
Dla osób, które chcą odpowiadać zgodnie z kodem statusu http, możesz skorzystać ze
ErrorController
sposobu:ResponseBean
Tutaj jest mój zwyczaj pojo za odpowiedź.źródło
Rozwiązanie z
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
i@EnableWebMvc @ControllerAdvice
pracował dla mnie z wiosennym Boot 1.3.1, natomiast nie działa na 1.2.7źródło