2015-06-18 35 views
8

Używam Spring boot do hostowania API REST. Zamiast standardowej odpowiedzi na błąd chciałbym zawsze wysłać odpowiedź JSON, nawet jeśli przeglądarka uzyskuje dostęp do adresu URL, a także niestandardową strukturę danych.Wiosenna inicjalizacja błędu 404 niestandardowa odpowiedź błędu ReST

Mogę to zrobić za pomocą @ControllerAdvice i @ExceptionHandler dla niestandardowych wyjątków. Ale nie mogę znaleźć dobrych sposobów, aby to zrobić dla standardowych i obsługiwanych błędów, takich jak 404 i 401.

Czy są jakieś dobre wzorce, jak to zrobić?

+0

Markus, czy moja odpowiedź rozwiązuje problem? Jeśli tak, zaakceptuj to. – Opal

Odpowiedz

0

Wygląda na to, że trzeba wprowadzić odpowiednio opatrzoną adnotację metodę, np. dla nieobsługiwany typ nośnika (415) będzie:

@ExceptionHandler(MethodArgumentNotValidException) 
    public ResponseEntity handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) { 
    logger.error('Caught exception', e) 
    def response = new ExceptionResponse(
      error: 'Validation error', 
      exception: e.class.name, 
      message: e.bindingResult.fieldErrors.collect { "'$it.field' $it.defaultMessage" }.join(', '), 
      path: req.servletPath, 
      status: BAD_REQUEST.value(), 
      timestamp: currentTimeMillis() 
    ) 
    new ResponseEntity<>(response, BAD_REQUEST) 
    } 

Jednak to może nie być możliwe, ponieważ 401 i 404 mogą być wyrzucane zanim dotrą DispatcherServlet - w tym przypadku ControllerAdvice nie będzie działać.

+0

To prawda, ale w tym przypadku interesują mnie przypadki 401 i 404, aby móc uzyskać jedną wspólną odpowiedź błędu dla wszystkich błędów dla klientów.Nie chcę mieć różnych struktur odpowiedzi na błąd, a także nigdy nie odpowiadać na nic innego niż na przykład JSON. – Markus

+0

Struktura, którą przedstawiłem, jest domyślną strukturą odpowiedzi na błędy JSON. Jeśli potrzebujesz tylko 401 lub 404, wystarczy dodać odpowiednie wyjątki zamiast "MethodArgumentNotValidException" i zmienić treść metody. – Opal

0

Można dodawać niestandardowe obiekty o wartości , które korelują z definicją strony błędu w pliku web.xml. Spring Boot provides an example ...

@Bean 
public EmbeddedServletContainerCustomizer containerCustomizer(){ 
    return new MyCustomizer(); 
} 

// ... 

private static class MyCustomizer implements EmbeddedServletContainerCustomizer { 

    @Override 
    public void customize(ConfigurableEmbeddedServletContainer container) { 
     container.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html")); 
     container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/not-found.html")); 
    } 

} 

EDIT: Chociaż myślę, że powyżej metoda będzie działać, jeśli uczynić strony błędów odpocząć kontrolerów, jeszcze łatwiejszy sposób byłoby to zwyczaj ErrorController jak ten poniżej. ..

@Bean 
public ErrorController errorController(ErrorAttributes errorAttributes) { 
    return new CustomErrorController(errorAttributes); 
} 

// ... 

public class CustomErrorController extends BasicErrorController { 

    public CustomErrorController(ErrorAttributes errorAttributes) { 
     super(errorAttributes); 
    } 

    @Override 
    @RequestMapping(value = "${error.path:/error}") 
    @ResponseBody 
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { 
     ResponseEntity<Map<String, Object>> error = super.error(request); 
     HttpStatus statusCode = error.getStatusCode(); 

     switch (statusCode) { 
     case NOT_FOUND: 
      return getMyCustomNotFoundResponseEntity(request); 
     case UNAUTHORIZED: 
      return getMyCustomUnauthorizedResponseEntity(request); 
     default: 
      return error; 
     } 
    } 
} 
+0

Tak, ale w tym przypadku błąd html nie jest odpowiedni, ponieważ są używane tylko klienty odpoczynku. – Markus

2

Błąd 404 jest obsługiwany przez DispatcherServlet. istnieje właściwość throwExceptionIfNoHandlerFound, którą można przesłonić.

W klasie aplikacji można utworzyć nowy Fasola:

@Bean 
DispatcherServlet dispatcherServlet() { 
    DispatcherServlet ds = new DispatcherServlet(); 
    ds.setThrowExceptionIfNoHandlerFound(true); 
    return ds; 
} 

... a potem złapać wyjątek NoHandlerFoundException w

@EnableWebMvc 
@ControllerAdvice 
public class GlobalControllerExceptionHandler { 
    @ExceptionHandler 
    @ResponseStatus(value=HttpStatus.NOT_FOUND) 
    @ResponseBody 
    public ErrorMessageResponse requestHandlingNoHandlerFound(final NoHandlerFoundException ex) { 
     doSomething(LOG.debug("text to log")); 
    } 
} 
+1

Lub użyj właściwości spring.mvc.throw-exception-if-no-handler-found = true zamiast niestandardowego komponentu bean DispatcherServlet. – Zotov

+1

Czy można to zrobić bez użycia adnotacji '@ EnableWebMvc' Nie chcę włączać Web MVC – Reema

3

Mam warunkiem roztworu próbki, w jaki sposób zastąpić odpowiedź dla 404 przypadku. Rozwiązanie jest dość dużo prosty i jestem delegowania przykładowy kod, ale można znaleźć więcej szczegółów na temat oryginalnego wątku: Spring Boot Rest - How to configure 404 - resource not found

Pierwszy: zdefiniować kontroler, który będzie przetwarzać przypadki błędów i zastąpić odpowiedź:

@ControllerAdvice 
public class ExceptionHandlerController { 

    @ExceptionHandler(NoHandlerFoundException.class) 
    @ResponseStatus(value= HttpStatus.NOT_FOUND) 
    @ResponseBody 
    public ErrorResponse requestHandlingNoHandlerFound() { 
     return new ErrorResponse("custom_404", "message for 404 error code"); 
    } 
} 

drugie: trzeba powiedzieć wiosna rzucić wyjątek w przypadku 404 (nie mógł rozwiązać obsługi):

@SpringBootApplication 
@EnableWebMvc 
public class Application { 

    public static void main(String[] args) { 
     ApplicationContext ctx = SpringApplication.run(Application.class, args); 

     DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet"); 
     dispatcherServlet.setThrowExceptionIfNoHandlerFound(true); 
    } 
} 
+1

Czy jest jakiś sposób, aby to zrobić bez korzystania z adnotacji '@ EnableWebMvc' Nie chcę włączyć Web MVC – Reema

+0

Hi @ Reema! Czy byłeś w stanie to zrobić bez '@ EnableWebMvc'? Chciałbym to zrobić bez tego również. –

+0

Nie, nie mam jeszcze żadnego rozwiązania! – Reema

0

Zarzut se patrz Spring Boot REST service exception handling. Pokazuje, jak nakazać dyspozytorowi, aby wyemitował wyjątki dla "nie znaleziono trasy", a następnie jak wychwycić te wyjątki. My (miejsce, w którym pracuję) używam tego w produkcji dla naszych usług REST już teraz.

Powiązane problemy