Spring Boot REST hizmet istisnası yönetimi


173

Büyük ölçekli bir REST hizmetleri sunucusu kurmaya çalışıyorum. Spring Boot 1.2.1 Spring 4.1.5 ve Java 8 kullanıyoruz. Denetleyicilerimiz @RestController ve standart @RequestMapping ek açıklamalarını uyguluyor.

Benim sorunum Spring Boot denetleyici istisnaları için varsayılan bir yönlendirme ayarlar /error. Dokümanlardan:

Spring Boot, varsayılan olarak tüm hataları mantıklı bir şekilde işleyen ve sunucu uygulaması kapsayıcısında 'genel' hata sayfası olarak kaydedilen bir / hata eşlemesi sağlar.

Node.js ile REST uygulamaları yazmanın yıllarından itibaren, bu benim için mantıklı değil. Bir hizmet uç noktasının oluşturduğu istisnalar yanıtta geri dönmelidir. Büyük olasılıkla Angular veya JQuery SPA tüketicisine yalnızca bir yanıt arayan ve bir yönlendirmede herhangi bir işlem yapamayan veya yapamayan bir yönlendirmeyi neden göndereceğinizi anlayamıyorum.

Ne yapmak istiyorum herhangi bir istisna alabilir - bir istek eşleme yönteminden kasıtlı olarak atılan veya Bahar tarafından otomatik olarak oluşturulan (istek yolu imzası için hiçbir işleyici yöntemi bulunmazsa 404) bir genel hata işleyicisi kurmak ve bir MVC yönlendirmeleri olmadan istemciye standart biçimlendirilmiş hata yanıtı (400, 500, 503, 404). Özellikle, hatayı alacağız, bir UUID ile NoSQL'e kaydedeceğiz, daha sonra istemciye JSON gövdesindeki günlük girişinin UUID'si ile doğru HTTP hata kodunu döndüreceğiz.

Belgeler bunun nasıl yapılacağı konusunda belirsiz. Bana göre, kendi ErrorController uygulamanızı oluşturmanız ya da ControllerAdvice'yi bir şekilde kullanmanız gerekiyor gibi görünüyor , ancak gördüğüm tüm örnekler hala yanıtı yardımcı olmayan bir tür hata eşlemesine yönlendirmeyi içeriyor. Diğer örnekler, yalnızca "Atılabilir" listelemek ve her şeyi almak yerine, işlemek istediğiniz her İstisna türünü listelemeniz gerektiğini gösterir.

Kimse neyi kaçırdığımı söyleyebilir veya Node.js'nin başa çıkmasının daha kolay olacağını zincirini önermeden bunu nasıl yapacağımı bana doğru yönde gösterebilir mi?


6
İstemciye hiçbir zaman gerçekte bir yönlendirme gönderilmez. Yönlendirme, sunucu uygulaması kabı (örn. Tomcat) tarafından dahili olarak gerçekleştirilir.
OrangeDog

1
İstisna işleyicilerimdeki @ResponseStatus ek açıklamalarını kaldırmak istediğim şeydi; bkz. stackoverflow.com/questions/35563968/…
pmorken

Yanıtlar:


131

Yeni cevap (2016-04-20)

Spring Boot 1.3.1'i Kullanın

Yeni Adım 1 - Uygulamaya aşağıdaki özellikleri eklemek kolay ve daha az müdahaleci: properties:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

Mevcut DispatcherServlet örneğini değiştirmekten çok daha kolay (aşağıdaki gibi)! - JO '

Tam bir RESTful Uygulaması ile çalışıyorsanız, statik önyüklemelerin otomatik eşlemesini devre dışı bırakmak çok önemlidir, çünkü Spring Boot'un statik kaynakları işlemek için varsayılan yapılandırmasını kullanıyorsanız, kaynak işleyici isteği işleyecektir (en son sipariş edilir ve / ** Bu, uygulamadaki başka bir işleyici tarafından ele alınmayan tüm istekleri aldığını gösterir), böylece dağıtımcı sunucu uygulaması bir istisna atma şansı elde etmez.


Yeni Cevap (2015-12-04)

Spring Boot 1.2.7 Kullanılması

Yeni Adım 1 - "throExceptionIfNoHandlerFound" bayrağını ayarlamak için çok daha az müdahaleci bir yol buldum. Uygulama başlatma sınıfınızdaki aşağıdaki DispatcherServlet değiştirme kodunu (Adım 1) bununla değiştirin:

@ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }

Bu durumda, Spring Boot çerçevesinin otomatik yapılandırmasını koruyan mevcut DispatcherServlet üzerindeki bayrağı ayarlıyoruz.

Bulduğum bir şey daha @EnableWebMvc ek açıklaması Spring Boot için ölümcül. Evet, bu ek açıklama, aşağıda açıklanan tüm denetleyici istisnalarını yakalama gibi şeyleri mümkün kılar, ancak Spring Boot'un normalde sağlayacağı yararlı otomatik yapılandırmanın LOT'unu da öldürür. Spring Boot kullanırken bu notu çok dikkatli kullanın.


Orijinal Yanıt:

Çok daha fazla araştırma ve burada yayınlanan çözümler (takip için teşekkürler!) Ve Bahar kodu içine az miktarda çalışma zamanı izleme sonra, nihayet tüm İstisnaları (Hatalar değil, ama okuma) işleyecek bir yapılandırma buldum sonra 404'ler dahil.

Adım 1 - SpringBoot'a "işleyici bulunamadı" durumlarında MVC'yi kullanmayı bırakmasını söyleyin. Spring'in istemciye "/ error" görünümüne yönlendirmek yerine bir istisna atmasını istiyoruz. Bunu yapmak için, yapılandırma sınıflarınızdan birinde bir girişinizin olması gerekir:

// NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig {
    @Bean  // Magic entry 
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
}

Bunun dezavantajı, varsayılan dağıtıcı sunucu uygulamasının yerini almasıdır. Bu henüz bizim için bir sorun olmadı, hiçbir yan etki veya uygulama problemi ortaya çıkmadı. Başka nedenlerden dolayı dağıtım programı sunucu uygulamasıyla başka bir şey yapacaksanız, burası bunları yapmanız gereken yerdir.

Adım 2 - Şimdi, bahar önyükleme, hiçbir işleyici bulunmadığında bir istisna atar, bu istisna, birleşik bir istisna işleyicisindeki diğerleriyle ele alınabilir:

@EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity<Object> handleControllerException(HttpServletRequest req, Throwable ex) {
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) {
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        }
        if(ex instanceof ServiceHttpException) {
            return new ResponseEntity<Object>(errorResponse,((ServiceHttpException)ex).getStatus());
        } else {
            return new ResponseEntity<Object>(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String,String> responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity<Object>(responseBody,HttpStatus.NOT_FOUND);
    }
    ...
}

Burada "@EnableWebMvc" ek açıklamasının önemli olduğunu unutmayın. Görünüşe göre bunların hiçbiri onsuz çalışmıyor. Ve işte bu - Bahar önyükleme uygulamanız şimdi yukarıdaki işleyici sınıfında 404'ler de dahil olmak üzere tüm istisnaları yakalayacak ve onlarla istediğiniz gibi yapabilirsiniz.

Son bir nokta - atılan Hataları yakalamak için bunu elde etmek için bir yol gibi görünmüyor. Hataları yakalamak ve yukarıdaki kod sonra başa çıkabilirim istisnalar açmak için yönlerini kullanarak tuhaf bir fikrim var, ama henüz bunu gerçekten denemek için zaman yoktu. Umarım bu birine yardımcı olur.

Herhangi bir yorum / düzeltme / geliştirme takdir edilecektir.


yeni bir dağıtıcı sunucu uygulaması fasulye oluşturmak yerine bayrağı bir post işlemcide çevirebilirsiniz: Sınıfınız BeanPostProcessor {... `public Object postProcessBeforeInitialization (Object bean, String beanName) BeansException {if (bean instanceof DispatcherServlet) {// bizim istisna işleyici ((DispatcherServlet) fasulye) başladı önce 404 olsun. setThrowExceptionIfNoHandlerFound (true); dönüş fasulyesi; } public Object postProcessAfterInitialization (Object bean, String beanName) BeansException {dönüş çekirdeği; }
wwadge

1
Bu sorun var ama DispatcherServlet özelleştirme benim için çalışmıyor. Boot'un bu ekstra fasulyeyi ve yapılandırmayı kullanması için ek bir sihir var mı?
IanGilham

3
@IanGilham Ben de bu Bahar Boot 1.2.7 ile çalışmak için alamadım. Sınıfta yerleştirilirlerse düzgün çalışmasına rağmen @ExceptionHandler, @ControllerAdvicesınıfa yerleştirirken çağrılan herhangi bir yöntem bile alamıyorum @RestController. @EnableWebMvcverildi @ControllerAdviceve @Configurationsınıf (her kombinasyon test edildi). Herhangi bir fikir veya çalışma örneği? //Endy Wilkinson
FrVaBe

1
Kim bu soruyu ve cevabı okursa, github'daki ilgili SpringBoot Sorununa bir göz atmalıdır .
FrVaBe

1
Emin değilim @agpt. 1.3.0'a kadar hareket edebileceğim ve kurulumumdaki etkinin ne olduğunu görebildiğim ve ne bulduğumu size bildirebileceğim dahili bir projem var.
ogradyjd

41

Spring Boot 1.4 ile daha kolay istisna yönetimi için yeni serin sınıflar eklendi.

@RestControllerAdviceİstisna işleme için yeni bir sağlanır @ControllerAdviceve ve birleşimidir @ResponseBody. Sen kaldırabilirsiniz @ResponseBodyüzerine @ExceptionHandlerbu yeni ek açıklama kullandığınızda yöntemle.

yani

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(value = { Exception.class })
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse unknownException(Exception ex, WebRequest req) {
        return new ApiErrorResponse(...);
    }
}

@EnableWebMvcApplication.properties öğesine ek açıklama ve aşağıdakiler ekleyerek 404 hatalarını işlemek için yeterliydi:
spring.mvc.throw-exception-if-no-handler-found=true

Kaynakları bulabilir ve burada bulabilirsiniz:
https://github.com/magiccrafter/spring-boot-exception-handling


7
Bu gerçekten yararlı, teşekkürler. Ama neden `` spring.mvc.throw-exception-if-no-handler-found = true`` ile `` EnableWebMvc '' e ihtiyacımız olduğunu anlamadım. Benim beklentim @RestControllerAdviceek yapılandırma olmadan tüm istisnaları ele oldu . Burada ne eksik?
fiskra

28

Bence ResponseEntityExceptionHandlergereksinimlerinizi karşılıyor. HTTP 400 için örnek bir kod parçası:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ExceptionHandler({HttpMessageNotReadableException.class, MethodArgumentNotValidException.class,
      HttpRequestMethodNotSupportedException.class})
  public ResponseEntity<Object> badRequest(HttpServletRequest req, Exception exception) {
    // ...
  }
}

Bunu kontrol edebilirsiniz yazı


6
Bu kodu daha önce gördüm ve uyguladıktan sonra sınıf denetleyici istek eşleme yöntemlerinde yükseltilmiş özel durumlar yakaladı. Bu, ResourceHttpRequestHandler.handleRequest yönteminde veya @EnableWebMvc ek açıklaması kullanılıyorsa, DispatcherServlet.noHandlerFound'da işlenen 404 hatalarını yakalamaz. 404'ler de dahil olmak üzere herhangi bir hatayı ele almak istiyoruz, ancak Spring Boot'un en son sürümü bunun nasıl yapılacağı konusunda inanılmaz derecede geniş görünüyor.
ogradyjd

HttpRequestMethodNotSupportedExceptionAynı kavanozu birden çok mikro hizmette işlemek ve eklemek için aynı yolu yazdım, bazı iş amaçları için yanıtta mikro hizmet takma adı adını yanıtlamamız gerekiyor. temel mikro hizmet adı / denetleyici adını almanın herhangi bir yolu var mı? HandlerMethodİstisna kaynaklı nereden Java yöntemi adı sağlayacak biliyorum . Ancak burada, yöntemlerin hiçbiri isteği almadı, bu nedenle HandlerMethodbaşlatılmayacak. Bunu çözmek için herhangi bir çözüm var mı?
Paramesh Korrakuti

Denetleyici tavsiyesi iyi bir yaklaşımdır, ancak istisnaların istisnai durumlarda oluşmaları gereken akışın bir parçası olmadığını daima unutmayın!
JorgeTovar

17

Bu daha eski bir soru olmasına rağmen, bu konudaki düşüncelerimi paylaşmak istiyorum. Umarım, bazılarınız için faydalı olacaktır.

Şu anda Spring Boot 1.5.2.RELEASE ile Spring Framework 4.3.7.RELEASE kullanan bir REST API oluşturuyorum. Java Config yaklaşımını kullanıyorum (XML yapılandırmasının aksine). Ayrıca, projem @RestControllerAdviceek açıklama kullanarak genel bir özel durum işleme mekanizması kullanıyor (daha sonra aşağıya bakın).

Projeminkilerle aynı gereksinimlere sahip: REST HTTP 404 Not FoundAPI'mın, mevcut olmayan bir URL'ye istek göndermeye çalıştığında API istemcisinin HTTP yanıtında eşlik eden bir JSON yükü ile birlikte dönmesini istiyorum . Benim durumumda, JSON yükü şöyle görünüyor (ki bu Spring Boot varsayılanından açıkça farklıdır, btw.):

{
    "code": 1000,
    "message": "No handler found for your request.",
    "timestamp": "2017-11-20T02:40:57.628Z"
}

Sonunda işe yaradı. Kısaca yapmanız gereken ana görevler şunlardır:

  • NoHandlerFoundExceptionAPI istemcileri için işleyici yöntemi bulunmayan URL'leri çağırırsa öğesinin atandığından emin olun (aşağıdaki 1. Adım'a bakın).
  • ApiErrorAPI istemcisine döndürülmesi gereken tüm verileri içeren özel bir hata sınıfı (benim durumumda ) oluşturun (2. adıma bakın).
  • NoHandlerFoundException API istemcisine tepki veren ve uygun bir hata iletisi döndüren bir özel durum işleyici oluşturun (3. adıma bakın).
  • Bunun için bir test yazın ve çalıştığından emin olun (bkz. Adım 4).

Tamam, şimdi ayrıntılara bakalım:

Adım 1: application.properties dosyasını yapılandırın

Projenin application.propertiesdosyasına aşağıdaki iki yapılandırma ayarını eklemek zorunda kaldım :

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

Bu, NoHandlerFoundExceptionistemcinin isteği işleyebilecek hiçbir denetleyici yönteminin bulunmadığı bir URL'ye erişmeye çalıştığı durumlarda a'nın atılmasını sağlar.

Adım 2: API Hataları için Sınıf Oluşturma

Eugen Paraschiv'in blogunda bu makalede önerilene benzer bir sınıf yaptım . Bu sınıf bir API hatasını temsil eder. Bu bilgiler, hata olması durumunda istemciye HTTP yanıt gövdesinde gönderilir.

public class ApiError {

    private int code;
    private String message;
    private Instant timestamp;

    public ApiError(int code, String message) {
        this.code = code;
        this.message = message;
        this.timestamp = Instant.now();
    }

    public ApiError(int code, String message, Instant timestamp) {
        this.code = code;
        this.message = message;
        this.timestamp = timestamp;
    }

    // Getters and setters here...
}

Adım 3: Genel İstisna İşleyici Oluşturma / Yapılandırma

İstisnalar işlemek için aşağıdaki sınıfı kullanın (basitlik için, ben ithalat ifadeleri, günlük kodu ve bazı diğer, ilgili olmayan kod parçaları kaldırdık):

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiError noHandlerFoundException(
            NoHandlerFoundException ex) {

        int code = 1000;
        String message = "No handler found for your request.";
        return new ApiError(code, message);
    }

    // More exception handlers here ...
}

4. Adım: Bir test yazın

Arıza durumunda bile API her zaman arayan istemciye doğru hata iletilerini döndürdüğünden emin olmak istiyorum. Böylece, böyle bir test yazdım:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SprintBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ActiveProfiles("dev")
public class GlobalExceptionHandlerIntegrationTest {

    public static final String ISO8601_DATE_REGEX =
        "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$";

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithMockUser(roles = "DEVICE_SCAN_HOSTS")
    public void invalidUrl_returnsHttp404() throws Exception {
        RequestBuilder requestBuilder = getGetRequestBuilder("/does-not-exist");
        mockMvc.perform(requestBuilder)
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.code", is(1000)))
            .andExpect(jsonPath("$.message", is("No handler found for your request.")))
            .andExpect(jsonPath("$.timestamp", RegexMatcher.matchesRegex(ISO8601_DATE_REGEX)));
    }

    private RequestBuilder getGetRequestBuilder(String url) {
        return MockMvcRequestBuilders
            .get(url)
            .accept(MediaType.APPLICATION_JSON);
    }

@ActiveProfiles("dev")Ek açıklama uzak bırakılabilir. Sadece farklı profillerle çalışırken kullanıyorum. RegexMatcherBir gelenektir hamcrest eşleştirici daha iyi sap damgası alanları için kullanın. İşte kodu ( burada buldum ):

public class RegexMatcher extends TypeSafeMatcher<String> {

    private final String regex;

    public RegexMatcher(final String regex) {
        this.regex = regex;
    }

    @Override
    public void describeTo(final Description description) {
        description.appendText("matches regular expression=`" + regex + "`");
    }

    @Override
    public boolean matchesSafely(final String string) {
        return string.matches(regex);
    }

    // Matcher method you can call on this matcher class
    public static RegexMatcher matchesRegex(final String string) {
        return new RegexMatcher(regex);
    }
}

Yanımda bazı notlar:

  • StackOverflow'daki diğer birçok gönderide, kullanıcılar @EnableWebMvcek açıklamayı ayarlamayı önerdi . Benim durumumda bu gerekli değildi.
  • Bu yaklaşım MockMvc ile iyi çalışır (yukarıdaki teste bakınız).

Bu benim için problemi çözdü. Sadece eklemek için, @ RestControllerAdvice ek açıklama eksikti, bu yüzden hepsini kontrol edecek ve bu hile yaptı @ ControllerAdvice ek açıklama ile birlikte ekledi.
PGMacDesign

13

Bu kod ne olacak? 404 hataları yakalamak için bir geri dönüş isteği eşleme kullanın.

@Controller
@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        //If exception has a ResponseStatus annotation then use its response code
        ResponseStatus responseStatusAnnotation = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);

        return buildModelAndViewErrorPage(request, response, ex, responseStatusAnnotation != null ? responseStatusAnnotation.value() : HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @RequestMapping("*")
    public ModelAndView fallbackHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return buildModelAndViewErrorPage(request, response, null, HttpStatus.NOT_FOUND);
    }

    private ModelAndView buildModelAndViewErrorPage(HttpServletRequest request, HttpServletResponse response, Exception ex, HttpStatus httpStatus) {
        response.setStatus(httpStatus.value());

        ModelAndView mav = new ModelAndView("error.html");
        if (ex != null) {
            mav.addObject("title", ex);
        }
        mav.addObject("content", request.getRequestURL());
        return mav;
    }

}

6

Varsayılan olarak Spring Boot json'a hata ayrıntılarını verir.

curl -v localhost:8080/greet | json_pp
[...]
< HTTP/1.1 400 Bad Request
[...]
{
   "timestamp" : 1413313361387,
   "exception" : "org.springframework.web.bind.MissingServletRequestParameterException",
   "status" : 400,
   "error" : "Bad Request",
   "path" : "/greet",
   "message" : "Required String parameter 'name' is not present"
}

Ayrıca her türlü istek eşleme hatası için de çalışır. Bu makaleye göz atın http://www.jayway.com/2014/10/19/spring-boot-error-responses/

Oluşturmak isterseniz NoSQL'e giriş yapın. @ControllerAdvice, oturum açacağınız yeri oluşturabilir ve ardından özel durumu yeniden atabilirsiniz. Belgede örnek https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc


Yukarıdaki gönderide yaptığım gibi bayrağı ayarlamadığınız sürece varsayılan DispatcherServlet, var olmayan bir eşleştirme isteği alındığında bir istisna atmak yerine MVC ile yeniden yönlendirme yapmak için sabit olarak kodlanmıştır.
ogradyjd

Ayrıca, ResponseEntityExceptionHandler sınıfını uygulamamızın nedeni, çıktı biçimini ve hata yığını izlerini NoSQL çözümüne kaydedip istemci için güvenli bir hata iletisi gönderebilmemizdir.
ogradyjd

6

@RestControllerAdvice, CrossfulApi ile İstisna'yı çapraz kesen bir endişe çözümü ile ele almak için Spring Framework 4.3'ün yeni bir özelliğidir:

 package com.khan.vaquar.exception;

import javax.servlet.http.HttpServletRequest;

import org.owasp.esapi.errors.IntrusionException;
import org.owasp.esapi.errors.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.khan.vaquar.domain.ErrorResponse;

/**
 * Handles exceptions raised through requests to spring controllers.
 **/
@RestControllerAdvice
public class RestExceptionHandler {

    private static final String TOKEN_ID = "tokenId";

    private static final Logger log = LoggerFactory.getLogger(RestExceptionHandler.class);

    /**
     * Handles InstructionExceptions from the rest controller.
     * 
     * @param e IntrusionException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IntrusionException.class)
    public ErrorResponse handleIntrusionException(HttpServletRequest request, IntrusionException e) {       
        log.warn(e.getLogMessage(), e);
        return this.handleValidationException(request, new ValidationException(e.getUserMessage(), e.getLogMessage()));
    }

    /**
     * Handles ValidationExceptions from the rest controller.
     * 
     * @param e ValidationException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ValidationException.class)
    public ErrorResponse handleValidationException(HttpServletRequest request, ValidationException e) {     
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);

        if (e.getUserMessage().contains("Token ID")) {
            tokenId = "<OMITTED>";
        }

        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(),
                                    e.getUserMessage());
    }

    /**
     * Handles JsonProcessingExceptions from the rest controller.
     * 
     * @param e JsonProcessingException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = JsonProcessingException.class)
    public ErrorResponse handleJsonProcessingException(HttpServletRequest request, JsonProcessingException e) {     
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(),
                                    e.getOriginalMessage());
    }

    /**
     * Handles IllegalArgumentExceptions from the rest controller.
     * 
     * @param e IllegalArgumentException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public ErrorResponse handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = UnsupportedOperationException.class)
    public ErrorResponse handleUnsupportedOperationException(HttpServletRequest request, UnsupportedOperationException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    /**
     * Handles MissingServletRequestParameterExceptions from the rest controller.
     * 
     * @param e MissingServletRequestParameterException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ErrorResponse handleMissingServletRequestParameterException( HttpServletRequest request, 
                                                                        MissingServletRequestParameterException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    /**
     * Handles NoHandlerFoundExceptions from the rest controller.
     * 
     * @param e NoHandlerFoundException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(value = NoHandlerFoundException.class)
    public ErrorResponse handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.NOT_FOUND.value(), 
                                    e.getClass().getSimpleName(), 
                                    "The resource " + e.getRequestURL() + " is unavailable");
    }

    /**
     * Handles all remaining exceptions from the rest controller.
     * 
     * This acts as a catch-all for any exceptions not handled by previous exception handlers.
     * 
     * @param e Exception
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public ErrorResponse handleException(HttpServletRequest request, Exception e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.error(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.INTERNAL_SERVER_ERROR.value(), 
                                    e.getClass().getSimpleName(), 
                                    "An internal error occurred");
    }   

}

3

REST kontrolörleri için kullanmanızı tavsiye ederim Zalando Problem Spring Web.

https://github.com/zalando/problem-spring-web

Spring Boot bazı otomatik yapılandırmalar yerleştirmeyi amaçlıyorsa, bu kitaplık özel durum işleme için daha fazlasını yapar. Sadece bağımlılığı eklemeniz gerekir:

<dependency>
    <groupId>org.zalando</groupId>
    <artifactId>problem-spring-web</artifactId>
    <version>LATEST</version>
</dependency>

Ardından, istisnalarınız için bir veya daha fazla tavsiye özelliği tanımlayın (veya varsayılan olarak sağlananları kullanın)

public interface NotAcceptableAdviceTrait extends AdviceTrait {

    @ExceptionHandler
    default ResponseEntity<Problem> handleMediaTypeNotAcceptable(
            final HttpMediaTypeNotAcceptableException exception,
            final NativeWebRequest request) {
        return Responses.create(Status.NOT_ACCEPTABLE, exception, request);
    }

}

Ardından özel durum işleme için denetleyici önerisini şu şekilde tanımlayabilirsiniz:

@ControllerAdvice
class ExceptionHandling implements MethodNotAllowedAdviceTrait, NotAcceptableAdviceTrait {

}

2

Http durum koduna göre yanıt vermek isteyen kişiler için şu ErrorControlleryolu kullanabilirsiniz :

@Controller
public class CustomErrorController extends BasicErrorController {

    public CustomErrorController(ServerProperties serverProperties) {
        super(new DefaultErrorAttributes(), serverProperties.getError());
    }

    @Override
    public ResponseEntity error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)){
            return ResponseEntity.status(status).body(ResponseBean.SERVER_ERROR);
        }else if (status.equals(HttpStatus.BAD_REQUEST)){
            return ResponseEntity.status(status).body(ResponseBean.BAD_REQUEST);
        }
        return super.error(request);
    }
}

ResponseBeanBurada yanıt için benim özel pojo olduğunu.


0

1.2.7 üzerinde çalışmazken Spring Boot 1.3.1 ile çözüm dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);ve @EnableWebMvc @ControllerAdvicebenim için çalıştı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.