स्प्रिंग बूट रेस्ट सर्विस अपवाद हैंडलिंग


172

मैं एक बड़े पैमाने पर REST सेवा सर्वर स्थापित करने का प्रयास कर रहा हूं। हम स्प्रिंग बूट 1.2.1 स्प्रिंग 4.1.5 और जावा 8 का उपयोग कर रहे हैं। हमारे नियंत्रक @RestController और मानक @RequestMapping एनोटेशन को लागू कर रहे हैं।

मेरी समस्या यह है कि स्प्रिंग बूट नियंत्रक अपवादों के लिए एक डिफ़ॉल्ट पुनर्निर्देशित करता है /error। डॉक्स से:

स्प्रिंग बूट डिफ़ॉल्ट रूप से / त्रुटि मैपिंग प्रदान करता है जो सभी त्रुटियों को समझदार तरीके से संभालता है, और यह सर्वलेट कंटेनर में 'वैश्विक' त्रुटि पृष्ठ के रूप में पंजीकृत है।

Node.js के साथ REST एप्लिकेशन लिखने के वर्षों से आ रहा है, यह मेरे लिए, कुछ भी लेकिन समझदार है। सेवा समापन बिंदु उत्पन्न करने वाला कोई भी अपवाद प्रतिक्रिया में वापस आ जाना चाहिए। मुझे समझ नहीं आ रहा है कि आप एक रीडायरेक्टर को क्यों भेजेंगे जो सबसे अधिक संभावना है एक कोणीय या JQuery एसपीए उपभोक्ता जो केवल एक उत्तर की तलाश में है और किसी रीडायरेक्ट पर कोई कार्रवाई नहीं कर सकता है या नहीं।

मैं जो करना चाहता हूं वह एक वैश्विक त्रुटि हैंडलर स्थापित करना है जो किसी भी अपवाद को ले सकता है - या तो उद्देश्यपूर्ण तरीके से एक मानचित्रण विधि से फेंक दिया जाता है या स्प्रिंग द्वारा 40-जनरेट किया जाता है (यदि अनुरोध पथ हस्ताक्षर के लिए कोई हैंडलर विधि नहीं मिली है), और वापस लौटें मानक स्वरूपित त्रुटि प्रतिक्रिया (400, 500, 503, 404) ग्राहक को बिना किसी एमवीसी पुनर्निर्देशन के। विशेष रूप से, हम त्रुटि को लेने जा रहे हैं, इसे UUID के साथ NoSQL में लॉग इन करें, फिर क्लाइंट को JSON बॉडी में लॉग एंट्री के UUID के साथ सही HTTP त्रुटि कोड पर लौटें।

यह करने के लिए डॉक्स अस्पष्ट हैं। यह मुझे लगता है कि आपको या तो अपना स्वयं का ErrorController कार्यान्वयन बनाना है या कुछ फैशन में नियंत्रक विज्ञापन का उपयोग करना है, लेकिन मैंने अभी भी देखे गए सभी उदाहरणों में कुछ प्रकार की त्रुटि मानचित्रण की प्रतिक्रिया को अग्रेषित करना शामिल है, जो मदद नहीं करता है। अन्य उदाहरण बताते हैं कि आपको प्रत्येक अपवाद प्रकार को सूचीबद्ध करना होगा जिसे आप "थ्रोबेलेबल" को सूचीबद्ध करने और सब कुछ प्राप्त करने के बजाय संभालना चाहते हैं।

क्या कोई मुझे बता सकता है कि मैं क्या याद कर रहा हूं, या मुझे इस दिशा में सही दिशा में इंगित करें कि यह कैसे बिना इस श्रृंखला के सुझाव के कि Node.js से निपटना आसान होगा?


6
ग्राहक को वास्तव में पुनर्निर्देशित नहीं किया जाता है। पुनर्निर्देशन आंतरिक रूप से सर्वलेट कंटेनर (जैसे टॉमकैट) द्वारा नियंत्रित किया जाता है।
ऑरेंजडॉग

1
मेरे अपवाद हैंडलर पर @ResponseStatus एनोटेशन को हटाना जो मुझे चाहिए था; देखें stackoverflow.com/questions/35563968/…
pmorken

जवाबों:


131

नया उत्तर (2016-04-20)

स्प्रिंग बूट का उपयोग 1.3.1

नया चरण 1 - आवेदन के लिए निम्नलिखित गुणों को जोड़ना आसान और कम घुसपैठ है।

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

मौजूदा DispatcherServlet उदाहरण को संशोधित करने की तुलना में बहुत आसान है (नीचे के रूप में)! - जो '

यदि पूर्ण रीस्टफुल एप्लिकेशन के साथ काम कर रहे हैं, तो स्थैतिक संसाधनों की स्वचालित मैपिंग को अक्षम करना बहुत महत्वपूर्ण है क्योंकि यदि आप स्थैतिक संसाधनों को संभालने के लिए स्प्रिंग बूट के डिफ़ॉल्ट कॉन्फ़िगरेशन का उपयोग कर रहे हैं तो संसाधन हैंडलर अनुरोध को संभाल लेगा (यह अंतिम आदेश दिया गया है और मैप किया गया है / ** जिसका अर्थ है कि यह किसी भी अनुरोध को चुनता है जिसे किसी भी अन्य हैंडलर द्वारा नियंत्रित नहीं किया गया है) इसलिए डिस्पैचर सर्वलेट को अपवाद फेंकने का मौका नहीं मिलता है।


नया उत्तर (2015-12-04)

स्प्रिंग बूट का उपयोग 1.2.7

नया चरण 1 - मुझे "throExceptionIfNoHandlerFound" ध्वज को स्थापित करने का बहुत कम घुसपैठ वाला तरीका मिला। नीचे दिए गए DispatcherServlet प्रतिस्थापन कोड को अपने चरण आरंभीकरण वर्ग में इसके साथ बदलें:

@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);
    }

इस मामले में, हम ध्वज को मौजूदा डिस्पैचरसर्वलेट पर सेट कर रहे हैं, जो स्प्रिंग बूट ढांचे द्वारा किसी भी ऑटो-कॉन्फ़िगरेशन को संरक्षित करता है।

एक और चीज़ जो मुझे मिली है - @EnableWebMvc एनोटेशन स्प्रिंग बूट के लिए घातक है। हां, यह एनोटेशन उन सभी नियंत्रक अपवादों को पकड़ने में सक्षम बनाता है जैसे कि नीचे वर्णित है, लेकिन यह सहायक ऑटो-कॉन्फ़िगरेशन का एक बहुत मारता है जो स्प्रिंग बूट सामान्य रूप से प्रदान करेगा। जब आप स्प्रिंग बूट का उपयोग करते हैं तो अत्यधिक सावधानी के साथ उस एनोटेशन का उपयोग करें।


मूल उत्तर:

बहुत अधिक शोध और बाद में यहां पोस्ट किए गए समाधानों पर (मदद के लिए धन्यवाद!) और स्प्रिंग कोड में रनटाइम ट्रेसिंग की कोई छोटी मात्रा नहीं है, मैंने आखिरकार एक कॉन्फ़िगरेशन पाया जो सभी अपवादों को संभाल लेगा (त्रुटियों को नहीं, लेकिन पढ़ें) 404s सहित।

चरण 1 - स्प्रिंगबूट को "हैंडलर नहीं मिला" स्थितियों के लिए एमवीसी का उपयोग बंद करने के लिए कहें। हम चाहते हैं कि स्प्रिंग ग्राहक के पास लौटने के बजाय एक अपवाद को फेंक दे, जिसे "/ त्रुटि" पर पुनर्निर्देशित किया जाए। ऐसा करने के लिए, आपको अपनी कॉन्फ़िगरेशन कक्षाओं में से एक में प्रवेश करना होगा:

// 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;
    }
}

इसका नकारात्मक पक्ष यह है कि यह डिफ़ॉल्ट डिस्पैचर सर्वलेट को बदल देता है। यह अभी तक हमारे लिए कोई समस्या नहीं है, कोई साइड इफेक्ट या निष्पादन समस्या नहीं है। यदि आप अन्य कारणों से डिस्पैचर सर्वलेट के साथ कुछ और करने जा रहे हैं, तो यह उनके लिए जगह है।

चरण 2 - अब जब स्प्रिंग बूट एक अपवाद को फेंक देगा जब कोई हैंडलर नहीं मिलता है, तो उस अपवाद को एकीकृत अपवाद हैंडलर में किसी अन्य के साथ संभाला जा सकता है:

@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);
    }
    ...
}

ध्यान रखें कि मुझे लगता है कि "@EnableWebMvc" एनोटेशन यहाँ महत्वपूर्ण है। ऐसा लगता है कि इसके बिना कोई भी काम नहीं करता है। और वह यह है - आपका स्प्रिंग बूट ऐप अब उपर्युक्त हैंडलर वर्ग में 404s सहित सभी अपवादों को पकड़ लेगा और आप कृपया उनके साथ ऐसा कर सकते हैं।

एक अंतिम बिंदु - फेंकी गई त्रुटियों को पकड़ने के लिए इसे प्राप्त करने का कोई तरीका प्रतीत नहीं होता है। मेरे पास त्रुटियों को पकड़ने के लिए पहलुओं का उपयोग करने का एक निराला विचार है और उन्हें अपवादों में बदलना है कि उपरोक्त कोड फिर से निपट सकता है, लेकिन मेरे पास अभी तक वास्तव में इसे लागू करने का प्रयास करने का समय नहीं है। आशा है कि यह किसी की मदद करता है।

किसी भी टिप्पणी / सुधार / संवर्द्धन की सराहना की जाएगी।


एक नया डिस्पैचर सर्वलेट बीन बनाने के बजाय आप फ़ेस पोस्ट प्रोसेसर में फ़्लैग कर सकते हैं: YourClass इम्प्लीमेंटेशन BeanPostProcessor {... 'public object postProcessBeforeInitialization (ऑब्जेक्ट बीन, स्ट्रिंग बिंगनाम) BeansException {if (बीन इंस्टाफॉस्फर डिस्पेरवेरलेटलेट) {// हम नहीं। 404 से पहले हमारे अपवाद हैंडलर में ((डिस्पैचरसर्वलेट) सेम) kset। } सेम वापस; } सार्वजनिक वस्तु postProcessAfterInitialization (ऑब्जेक्ट बीन, स्ट्रिंग बीननाम) बीन एक्ससेप्शन फेंकता है; }
वेज

1
मुझे यह समस्या है लेकिन DispatcherServlet को अनुकूलित करना मेरे लिए काम नहीं करता है। क्या इस अतिरिक्त बीन और विन्यास का उपयोग करने के लिए बूट के लिए किसी अतिरिक्त जादू की आवश्यकता है?
इगनिगहम सेप

3
@IanGilham मुझे भी स्प्रिंग बूट 1.2.7 के साथ काम करने के लिए नहीं मिलता है। यहां तक ​​कि मुझे कक्षा @ExceptionHandlerमें रखने के दौरान कोई भी विधि नहीं मिलती है , @ControllerAdviceहालांकि वे @RestControllerकक्षा में रखे जाने पर ठीक से काम करते हैं । @EnableWebMvcपर है @ControllerAdviceऔर @Configurationवर्ग (मैं हर संयोजन का परीक्षण किया)। कोई विचार या काम करने का उदाहरण? // @Andy Wilkinson
FrVaBe

1
जो कोई भी इस प्रश्न और उत्तर को पढ़ता है, उसे गीथूब पर संबंधित स्प्रिंगबूट अंक पर एक नजर डालनी चाहिए
FrVaBe

1
यकीन नहीं होता @agpt। मेरे पास एक आंतरिक परियोजना है जिसे मैं 1.3.0 तक ले जा सकता हूं और देख सकता हूं कि मेरे सेटअप पर क्या प्रभाव पड़ता है और आपको पता है कि मुझे क्या मिला।
ऑग्रेडाइज्ड

41

स्प्रिंग बूट के साथ आसान अपवाद हैंडलिंग के लिए नए शांत वर्गों को जोड़ा गया जो बॉयलरप्लेट कोड को हटाने में मदद करता है।

@RestControllerAdviceअपवाद हैंडलिंग के लिए एक नया प्रदान किया जाता है, यह @ControllerAdviceऔर का संयोजन है @ResponseBody। इस नए एनोटेशन का उपयोग करने @ResponseBodyपर आप @ExceptionHandlerविधि को हटा सकते हैं ।

अर्थात

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

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

404 त्रुटियों को जोड़ने के लिए @EnableWebMvcएनोटेशन और अनुप्रयोग में निम्नलिखित को जोड़ना।
spring.mvc.throw-exception-if-no-handler-found=true

आप यहां स्रोतों से पा सकते हैं और खेल सकते हैं:
https://github.com/magiccrafter/spring-boot-exception-handling


7
यह वास्तव में उपयोगी है, धन्यवाद। लेकिन मुझे यह नहीं पता था कि हमें `स्प्रिंग.वी.थ्रो-अपवाद-अगर-नहीं-हैंडलर-पाया = सही` के साथ `@EnableWebMvc` की आवश्यकता है। मेरी अपेक्षा @RestControllerAdviceअतिरिक्त विन्यास के बिना सभी अपवादों को संभालने की थी । मुझे यहां क्या समझ नहीं आ रहा है?
फिकरा

28

मुझे लगता ResponseEntityExceptionHandlerहै कि आपकी आवश्यकताओं को पूरा करता है। HTTP 400 के लिए कोड का एक नमूना टुकड़ा:

@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) {
    // ...
  }
}

आप इस पोस्ट को देख सकते हैं


6
मैंने इस कोड को पहले देखा है, और इसे लागू करने के बाद, कक्षा ने नियंत्रक अनुरोध करने के तरीकों में उठाए गए अपवादों को पकड़ लिया। यह अभी भी 404 त्रुटियों को नहीं पकड़ता है, जो कि DispatchSexletServlet.noHandlerFound में, यदि @EnableWebMvc एनोटेशन का उपयोग किया जाता है, तो ResourceHttpRequestHandler.handleRequest विधि में नियंत्रित किया जा रहा है। हम 404 सहित किसी भी त्रुटि को संभालना चाहते हैं, लेकिन स्प्रिंग बूट का नवीनतम संस्करण ऐसा करने के लिए अविश्वसनीय रूप से अप्रचलित प्रतीत होता है।
ऑग्रेडाइज्ड

मैंने HttpRequestMethodNotSupportedExceptionकई माइक्रो-सेवाओं में एक ही जार को संभालने और प्लग करने का एक ही तरीका लिखा था , कुछ व्यावसायिक उद्देश्य के लिए हमें प्रतिक्रिया में माइक्रो-सर्विस उर्फ ​​नाम का जवाब देना होगा। क्या कोई तरीका है जिससे हम अंतर्निहित माइक्रो-सेवा नाम / नियंत्रक नाम प्राप्त कर सकते हैं? मुझे पता है HandlerMethodकि अपवाद उत्पन्न होने पर जावा विधि नाम प्रदान करेगा। लेकिन यहाँ, किसी भी तरीके से अनुरोध प्राप्त नहीं हुआ है, इसलिए HandlerMethodइसे प्रारंभ नहीं किया जाएगा। तो क्या इसका हल निकालने का कोई उपाय है?
परमीश कोरकुट्टी

नियंत्रक सलाह एक अच्छा तरीका है, लेकिन हमेशा याद रखें कि अपवाद प्रवाह का हिस्सा नहीं हैं जो उन्हें असाधारण मामलों में होना चाहिए!
जॉर्जटॉवर

17

हालांकि यह एक पुराना सवाल है, मैं इस पर अपने विचार साझा करना चाहूंगा। मुझे आशा है, कि यह आप में से कुछ के लिए उपयोगी होगा।

मैं वर्तमान में एक REST API का निर्माण कर रहा हूं जो स्प्रिंग बूट 1.5.2 का उपयोग करता है। कृपया स्प्रिंग फ्रेमवर्क के साथ 4.3.7.RELEASE करें। मैं जावा विन्यास दृष्टिकोण का उपयोग करता हूं (एक्सएमएल कॉन्फ़िगरेशन के विपरीत)। इसके अलावा, मेरी परियोजना @RestControllerAdviceएनोटेशन (नीचे बाद में देखें) का उपयोग करके एक वैश्विक अपवाद हैंडलिंग तंत्र का उपयोग करती है ।

मेरी परियोजना की आपकी जैसी ही आवश्यकताएं हैं: मैं चाहता हूं कि मेरा REST API HTTP 404 Not FoundHTTP क्लाइंट की प्रतिक्रिया में JSON पेलोड के साथ वापस लौटे जब यह एक URL के लिए एक अनुरोध भेजने की कोशिश करता है जो मौजूद नहीं है। मेरे मामले में, JSON पेलोड इस तरह दिखता है (जो स्पष्ट रूप से स्प्रिंग बूट डिफ़ॉल्ट, btw से अलग है।)।

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

मैंने आखिरकार यह काम कर दिया। यहां मुख्य कार्य हैं जिन्हें आपको संक्षेप में करने की आवश्यकता है:

  • सुनिश्चित करें कि NoHandlerFoundExceptionअगर एपीआई क्लाइंट के लिए कोई हैंडलर विधि मौजूद है तो URL फेंक दिया जाता है (नीचे चरण 1 देखें)।
  • एक कस्टम त्रुटि वर्ग बनाएं (मेरे मामले में ApiError) जिसमें वह सभी डेटा है जो एपीआई क्लाइंट को वापस किया जाना चाहिए (चरण 2 देखें)।
  • अपवाद हैंडलर बनाएँ, जो NoHandlerFoundException API क्लाइंट पर एक उचित त्रुटि संदेश देता है और उस पर प्रतिक्रिया देता है (चरण 3 देखें)।
  • इसके लिए एक परीक्षण लिखें और सुनिश्चित करें कि यह काम करता है (चरण 4 देखें)।

ठीक है, अब विवरण पर:

चरण 1: एप्लिकेशन को कॉन्फ़िगर करें

मुझे प्रोजेक्ट की application.propertiesफ़ाइल में निम्नलिखित दो कॉन्फ़िगरेशन सेटिंग्स जोड़नी थीं :

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

यह सुनिश्चित करता है, उन NoHandlerFoundExceptionमामलों में फेंक दिया जाता है जहां एक ग्राहक एक URL तक पहुंचने की कोशिश करता है जिसके लिए कोई नियंत्रक विधि मौजूद नहीं है जो अनुरोध को संभालने में सक्षम होगा।

चरण 2: एपीआई त्रुटियों के लिए एक वर्ग बनाएं

मैंने यूजीन पाराशिव के ब्लॉग पर इस लेख में सुझाए गए एक वर्ग के समान बनाया । यह वर्ग एक एपीआई त्रुटि का प्रतिनिधित्व करता है। यह जानकारी क्लाइंट को त्रुटि के मामले में HTTP प्रतिसाद निकाय में भेजी जाती है।

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...
}

चरण 3: एक वैश्विक अपवाद हैंडलर बनाएँ / कॉन्फ़िगर करें

मैं अपवादों को संभालने के लिए निम्न वर्ग का उपयोग करता हूं (सादगी के लिए, मैंने आयात स्टेटमेंट, लॉगिंग कोड और कुछ अन्य, गैर-प्रासंगिक कोड के टुकड़े हटा दिए हैं):

@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: एक परीक्षण लिखें

मैं यह सुनिश्चित करना चाहता हूं, एपीआई हमेशा कॉलिंग क्लाइंट को सही त्रुटि संदेश देता है, यहां तक ​​कि विफलता के मामले में भी। इस प्रकार, मैंने एक परीक्षा इस तरह लिखी:

@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")एनोटेशन दूर छोड़ा जा सकता है। मैं इसका उपयोग केवल इसलिए करता हूं क्योंकि मैं विभिन्न प्रोफाइलों के साथ काम करता हूं। RegexMatcherएक कस्टम है Hamcrest मिलान मैं बेहतर संभाल टाइमस्टैम्प क्षेत्रों के लिए इस्तेमाल करते हैं। यहाँ कोड है (मैंने इसे यहाँ पाया ):

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);
    }
}

मेरी ओर से कुछ और नोट:

  • StackOverflow पर कई अन्य पोस्ट में, लोगों ने @EnableWebMvcएनोटेशन सेट करने का सुझाव दिया । मेरे मामले में यह जरूरी नहीं था।
  • यह दृष्टिकोण MockMvc के साथ अच्छी तरह से काम करता है (ऊपर परीक्षण देखें)।

इससे मेरे लिए समस्या हल हो गई। बस जोड़ने के लिए, मुझे @ RestControllerAdvice एनोटेशन याद आ रहा था, इसलिए मैंने @ कंट्रोलर एडवाइज एनोटेशन के साथ जोड़ा ताकि यह सभी को संभाल ले और इसने यह ट्रिक अपनाई।
PGMacDesign

13

इस कोड के बारे में क्या? मैं 404 त्रुटियों को पकड़ने के लिए फॉलबैक अनुरोध मैपिंग का उपयोग करता हूं।

@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

डिफ़ॉल्ट रूप से स्प्रिंग बूट त्रुटि विवरण के साथ json देता है।

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"
}

यह सभी प्रकार के अनुरोध मैपिंग त्रुटियों के लिए भी काम करता है। इस लेख की जाँच करें http://www.jayway.com/2014/10/19/spring-boot-error-responses/

अगर आप इसे NoSQL में बनाना चाहते हैं। आप @ControllerAdvice बना सकते हैं जहां आप इसे लॉग इन करेंगे और फिर अपवाद को फिर से फेंक देंगे। प्रलेखन में उदाहरण है https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc


डिफ़ॉल्ट डिस्पैचरसर्वलेट को एमवीसी के साथ पुनर्निर्देशित करने के लिए हार्डकोड किया जाता है, जब एक गैर-मौजूद मानचित्रण के लिए अनुरोध प्राप्त होता है, तो अपवाद को फेंकने के बजाय - जब तक आप ध्वज को सेट नहीं करते हैं जैसा कि मैंने ऊपर पोस्ट में किया है।
ओगराडजिड

इसके अलावा, हम ResponseEntityExceptionHandler वर्ग को लागू करने का कारण यह है कि हम आउटपुट के प्रारूप को नियंत्रित कर सकते हैं और एक NoSQL समाधान के लिए स्टैक के निशान को लॉग कर सकते हैं और फिर क्लाइंट-सुरक्षित त्रुटि संदेश भेज सकते हैं।

6

@RestControllerAdvice स्प्रिंग फ्रेमवर्क 4.3 की एक नई विशेषता है जो क्रॉस-कटिंग नियंत्रण समाधान द्वारा रेस्टफुलएपीआई के साथ अपवाद को संभालने के लिए है:

 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 नियंत्रकों के लिए, मैं उपयोग करने की सलाह दूंगा Zalando Problem Spring Web

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

यदि स्प्रिंग बूट का उद्देश्य कुछ ऑटो-कॉन्फ़िगरेशन को एम्बेड करना है, तो यह लाइब्रेरी अपवाद से निपटने के लिए अधिक करती है। आपको केवल निर्भरता जोड़ने की आवश्यकता है:

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

और फिर अपने अपवादों के लिए एक या एक से अधिक सलाह लक्षणों को परिभाषित करें (या डिफ़ॉल्ट रूप से प्रदान किए गए उपयोग करें)

public interface NotAcceptableAdviceTrait extends AdviceTrait {

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

}

तब आप अपवाद से निपटने के लिए नियंत्रक सलाह को निम्नानुसार परिभाषित कर सकते हैं:

@ControllerAdvice
class ExceptionHandling implements MethodNotAllowedAdviceTrait, NotAcceptableAdviceTrait {

}

2

उन लोगों के लिए जो http स्थिति कोड के अनुसार प्रतिक्रिया देना चाहते हैं, आप इसका उपयोग कर सकते हैं ErrorController:

@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);
    }
}

ResponseBeanयहाँ प्रतिक्रिया के लिए अपने कस्टम POJO है।


0

समाधान के साथ dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);और @EnableWebMvc @ControllerAdvice मेरे लिए स्प्रिंग बूट 1.3.1 के साथ काम किया, जबकि 1.2.7 पर काम नहीं कर रहा था

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.