स्प्रिंग-एमवीसी नियंत्रक में ट्रिगर 404?


193

मैं एक स्प्रिंग कैसे प्राप्त करूं 404 को ट्रिगर करने के लिए 3.0 कंट्रोलर ?

मेरे पास कंट्रोलर है @RequestMapping(value = "/**", method = RequestMethod.GET)और कुछ URL के लिए कंट्रोलर एक्सेस कर रहा है, मैं चाहता हूं कि कंटेनर 404 के साथ आए।

जवाबों:


326

स्प्रिंग 3.0 के बाद से आप @ResponseStatusएनोटेशन के साथ घोषित अपवाद भी फेंक सकते हैं :

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    ...
}

@Controller
public class SomeController {
    @RequestMapping.....
    public void handleCall() {
        if (isFound()) {
            // whatever
        }
        else {
            throw new ResourceNotFoundException(); 
        }
    }
}

2
दिलचस्प। क्या आप निर्दिष्ट कर सकते हैं कि थ्रस्ट साइट पर कौन सा HttpStatus उपयोग करना है (अर्थात इसे अपवाद वर्ग में संकलित नहीं किया गया है)?
मैट बी

1
@mattb: मुझे लगता @ResponseStatusहै कि आप सभी के साथ दृढ़ता से टाइप, अच्छी तरह से नामित अपवाद वर्गों के एक पूरे समूह को परिभाषित करते हैं @ResponseStatus। इस तरह, आप अपने कंट्रोलर कोड को HTTP स्टेटस कोड के विस्तार से अलग कर लेते हैं।
स्कफमैन

9
क्या यह त्रुटि के बारे में अधिक विवरण वाले निकाय को वापस करने के समर्थन के लिए बढ़ाया जा सकता है?
टॉम

7
@ टोम:@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
नेलगुन

6
यदि आप केवल संसाधन नियंत्रण के लिए इस ResourceNotFound अपवाद का उपयोग करते हैं, तो शायद ResourceNotFound.fillInStackTrace()खाली क्रियान्वयन के साथ ओवरराइड करना एक अच्छा विचार है ।
राल्फ

38

स्प्रिंग 5.0 से शुरू, आपको अतिरिक्त अपवाद बनाने की आवश्यकता नहीं है:

throw new ResponseStatusException(NOT_FOUND, "Unable to find resource");

इसके अलावा, आप एक, बिल्ट-इन अपवाद के साथ कई परिदृश्यों को कवर कर सकते हैं और आपके पास अधिक नियंत्रण है।

और देखें:


36

अपने विधि हस्ताक्षर को फिर से लिखें ताकि यह HttpServletResponseएक पैरामीटर के रूप में स्वीकार हो , ताकि आप इस setStatus(int)पर कॉल कर सकें।

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments


8
यह एकमात्र सही उत्तर है अगर कोई http अनुरोधकर्ता को यह बताने का तरीका खोज रहा है कि उन्होंने गलती ओप्स टीम को बाढ़ नहीं करते हुए एक अपवाद के समूह के साथ की है जो वे ठीक नहीं कर सकते हैं।
एलेक्स आर

4
setStatus(int)javadoc में निम्नानुसार है: यदि इस पद्धति का उपयोग त्रुटि कोड सेट करने के लिए किया जाता है, तो कंटेनर का त्रुटि पृष्ठ तंत्र ट्रिगर नहीं होगा। यदि कोई त्रुटि है और फोन करने वाले को वेब एप्लिकेशन में परिभाषित त्रुटि पृष्ठ को आमंत्रित sendErrorकरना है , तो इसके बजाय इसका उपयोग किया जाना चाहिए।
फिलिप जियोसेफी

@ एएक्सआरआर हैंडल किए गए अपवादों को ऑप्स टीम को बाढ़ नहीं करना चाहिए। यदि वे हैं, तो लॉगिंग गलत तरीके से की जा रही है।
राएदावलड

25

मैं उल्लेख करना चाहूंगा कि स्प्रिंग द्वारा प्रदान की गई डिफ़ॉल्ट रूप से 404 के लिए अपवाद (न केवल) है। देखें स्प्रिंग प्रलेखन जानकारी के लिए। तो अगर आपको अपने स्वयं के अपवाद की आवश्यकता नहीं है, तो आप बस ऐसा कर सकते हैं:

 @RequestMapping(value = "/**", method = RequestMethod.GET)
 public ModelAndView show() throws NoSuchRequestHandlingMethodException {
    if(something == null)
         throw new NoSuchRequestHandlingMethodException("show", YourClass.class);

    ...

  }

11
यह एक विशिष्ट मामले के लिए माना जाता है - जब स्प्रिंग को एक हैंडलर नहीं मिल सकता है। सवाल में मामला यह है कि जब स्प्रिंग एक हैंडलर पा सकता है , लेकिन उपयोगकर्ता किसी अन्य कारण से 404 लौटना चाहता है।
रॉय Truelove

2
जब हैंडलर विधि के लिए मेरी ulr मैपिंग गतिशील हो तो मैं इसका उपयोग कर रहा हूँ। जब अस्तित्व के आधार पर अस्तित्व नहीं है @PathVariable, तो मेरे दृष्टिकोण से निपटने का कोई अनुरोध नहीं है। क्या आपको लगता है कि अपने स्वयं के अपवाद के साथ एनोटेट का उपयोग करना बेहतर / क्लीनर है @ResponseStatus(value = HttpStatus.NOT_FOUND) ?
मिशाल.क्रूज़मैन

1
आपके मामले में यह ठीक लगता है, लेकिन मुझे नहीं पता कि मैं उन सभी मामलों को संभालने के लिए आपके द्वारा प्रदान किए गए लिंक में पाए गए अपवादों की सिफारिश करूंगा जहां एक अपवाद आवश्यक है - कभी-कभी आपको अपना खुद का बनाना चाहिए।
रॉय Truelove

खैर, स्प्रिंग ने एक अपवाद प्रदान किया और केवल 404 के लिए। उन्होंने इसे 404 अपवाद का नाम दिया या एक बनाया। लेकिन जैसा कि यह अब है, मुझे लगता है कि जब भी आपको 404 की आवश्यकता हो तो इसे फेंक देना ठीक है।
ऑटो

ठीक है, तकनीकी रूप से बोलना ठीक है - आप 404 स्टेटस हेडर भेजेंगे। लेकिन स्वचालित त्रुटि संदेश - प्रतिक्रिया सामग्री - "नाम के साथ कोई अनुरोध हैंडलिंग विधि नहीं है ..." जो संभवतः वह कुछ नहीं है जो आप उपयोगकर्ता को दिखाना चाहते हैं।
ओली

24

स्प्रिंग 3.0.2 के बाद से आप रेस्पोंसेंटिटी <T> को नियंत्रक की विधि के परिणामस्वरूप वापस कर सकते हैं :

@RequestMapping.....
public ResponseEntity<Object> handleCall() {
    if (isFound()) {
        // do what you want
        return new ResponseEntity<>(HttpStatus.OK);
    }
    else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

(ResponseEntity <T> @ResponseBody एनोटेशन की तुलना में अधिक लचीला है - एक और प्रश्न देखें )


2
लचीला-पाठ्यक्रम लेकिन घोषणात्मक प्रोग्रामिंग के लाभों को हरा देता है
रोहनगरवाल

3
यदि आप संतरी या PROD में समान का उपयोग कर रहे हैं, और इसे उन त्रुटियों के साथ स्पैम नहीं करना चाहते हैं जो कोई वास्तविक त्रुटि नहीं हैं, तो यह समाधान इस गैर-असाधारण स्थिति के अपवादों का उपयोग करने वाले की तुलना में बहुत बेहतर है।
टोबियास हरमन

शरीर (अपनी वास्तविक वस्तु के साथ) को पॉप्युलेट करना न भूलें। जेनेरिक "ऑब्जेक्ट" उदाहरण: ऑब्जेक्ट रिटर्न ItemBody = नई ऑब्जेक्ट (); वापसी ResponseEntity.status (HttpStatus.OK) .body (returnItemBody);
ग्रेनडाकर

16

आप @ControllerAdvice का उपयोग कर सकते हैं अपने अपवादों को संभालने के का , डिफ़ॉल्ट व्यवहार @ControllerAdvice एनोटेट वर्ग सभी ज्ञात नियंत्रकों की सहायता करेगा।

इसलिए यह तब कहा जाएगा जब आपके पास कोई नियंत्रक 404 त्रुटि है।

निम्नलिखित की तरह:

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.NOT_FOUND)  // 404
    @ExceptionHandler(Exception.class)
    public void handleNoTFound() {
        // Nothing to do
    }
}

और अपने web.xml में इस 404 रिस्पॉन्स एरर को निम्न की तरह मैप करें:

<error-page>
        <error-code>404</error-code>
        <location>/Error404.html</location>
</error-page>

उम्मीद है की वो मदद करदे ।


2
आपने 404 स्थिति कोड के साथ अपवाद (और उपवर्ग) के अपवादों को मैप किया है। क्या आपने कभी सोचा कि आंतरिक सर्वर त्रुटियां हैं? आप अपने GlobalControllerExceptionHandler में उन लोगों को कैसे संभालने की योजना बनाते हैं?
रोहनगरवाल

यह REST नियंत्रकों के लिए काम नहीं किया, एक खाली प्रतिक्रिया देता है।
रिस्टेक्स

10

यदि आपका नियंत्रक तरीका फ़ाइल संभालने जैसी किसी चीज़ के लिए है तो ResponseEntityबहुत आसान है:

@Controller
public class SomeController {
    @RequestMapping.....
    public ResponseEntity handleCall() {
        if (isFound()) {
            return new ResponseEntity(...);
        }
        else {
            return new ResponseEntity(404);
        }
    }
}

9

जबकि चिह्नित उत्तर सही है, बिना अपवाद के इसे प्राप्त करने का एक तरीका है। सेवा Optional<T>खोजी गई वस्तु पर वापस लौट रही है और HttpStatus.OKयदि खाली है तो उसे 404 और यदि मिला तो मैप किया गया है।

@Controller
public class SomeController {

    @RequestMapping.....
    public ResponseEntity<Object> handleCall() {
        return  service.find(param).map(result -> new ResponseEntity<>(result, HttpStatus.OK))
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }
}

@Service
public class Service{

    public Optional<Object> find(String param){
        if(!found()){
            return Optional.empty();
        }
        ...
        return Optional.of(data); 
    }

}

मैं इस दृष्टिकोण को सामान्य रूप से पसंद करता हूं, लेकिन वैकल्पिक का उपयोग करना कभी-कभी एक विरोधी पैटर्न होता है। और संग्रह लौटने पर जटिल हो जाता है।
jfzr

7

मैं इस तरह HttpClientErrorException को फेंकने की सलाह दूंगा

@RequestMapping(value = "/sample/")
public void sample() {
    if (somethingIsWrong()) {
        throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
    }
}

आपको याद रखना चाहिए कि सर्वलेट आउटपुट स्ट्रीम में कुछ भी लिखे जाने से पहले ही ऐसा किया जा सकता है।


4
वह अपवाद स्प्रिंग HTTP क्लाइंट द्वारा फेंका गया है। स्प्रिंग MVC उस अपवाद को नहीं पहचानता है। आप किस स्प्रिंग संस्करण का उपयोग कर रहे हैं? क्या आपको उस अपवाद के साथ ४०४ मिल रहे हैं?
एडुआर्डो

1
इसके कारण स्प्रिंग बूट वापस आ जाता है:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
स्लिम

यह HTTP क्लाइंट के लिए अपवाद है, नियंत्रक के लिए नहीं। इसलिए इसे निर्दिष्ट संदर्भ में उपयोग करना अनुचित है।
एलेक्सी

3

यह थोड़ा लेट है, लेकिन यदि आप स्प्रिंग डेटा रीस्ट का उपयोग कर रहे हैं तो पहले से ही है। org.springframework.data.rest.webmvc.ResourceNotFoundException यह @ResponseStatusएनोटेशन का भी उपयोग करता है । अब कस्टम रनटाइम अपवाद बनाने की कोई आवश्यकता नहीं है।


2

इसके अलावा अगर आप अपने कंट्रोलर से 404 स्टेटस वापस करना चाहते हैं तो आपको बस यही करना है

@RequestMapping(value = "/somthing", method = RequestMethod.POST)
@ResponseBody
public HttpStatus doSomthing(@RequestBody String employeeId) {
    try{
  return HttpStatus.OK;
    } 
    catch(Exception ex){ 
  return HttpStatus.NOT_FOUND;
    }
}

ऐसा करने पर आपको उस स्थिति में 404 त्रुटि मिलेगी जब आप अपने नियंत्रक से 404 वापस करना चाहते हैं।


0

बस आप त्रुटि कोड और 404 त्रुटि पेज जोड़ने के लिए web.xml का उपयोग कर सकते हैं। लेकिन सुनिश्चित करें कि 404 त्रुटि पेज को WEB-INF के तहत पता नहीं लगाना चाहिए।

<error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
</error-page>

यह इसे करने का सबसे सरल तरीका है लेकिन इसकी कुछ सीमा है। मान लीजिए अगर आप इस पेज के लिए वही स्टाइल जोड़ना चाहते हैं जो आपने अन्य पेज जोड़े हैं। इस तरह से आप ऐसा नहीं कर सकते। आपको उपयोग करना होगा@ResponseStatus(value = HttpStatus.NOT_FOUND)


यह करने का तरीका है, लेकिन HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;नियंत्रक कोड से इसके साथ विचार करें । अब बाहर से प्रतिक्रिया सामान्य 404 से अलग नहीं दिखती है जो किसी भी नियंत्रक से नहीं टकराती है।
डैरिल मील्स

यह एक 404 को ट्रिगर नहीं करता है, यह सिर्फ इसे संभालता है अगर एक होता है
एलेक्स आर

0

सेटिंग के साथ web.xml कॉन्फ़िगर करें

<error-page>
    <error-code>500</error-code>
    <location>/error/500</location>
</error-page>

<error-page>
    <error-code>404</error-code>
    <location>/error/404</location>
</error-page>

नया कंट्रोलर बनाएं

   /**
     * Error Controller. handles the calls for 404, 500 and 401 HTTP Status codes.
     */
    @Controller
    @RequestMapping(value = ErrorController.ERROR_URL, produces = MediaType.APPLICATION_XHTML_XML_VALUE)
    public class ErrorController {


        /**
         * The constant ERROR_URL.
         */
        public static final String ERROR_URL = "/error";


        /**
         * The constant TILE_ERROR.
         */
        public static final String TILE_ERROR = "error.page";


        /**
         * Page Not Found.
         *
         * @return Home Page
         */
        @RequestMapping(value = "/404", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
        public ModelAndView notFound() {

            ModelAndView model = new ModelAndView(TILE_ERROR);
            model.addObject("message", "The page you requested could not be found. This location may not be current.");

            return model;
        }

        /**
         * Error page.
         *
         * @return the model and view
         */
        @RequestMapping(value = "/500", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
        public ModelAndView errorPage() {
            ModelAndView model = new ModelAndView(TILE_ERROR);
            model.addObject("message", "The page you requested could not be found. This location may not be current, due to the recent site redesign.");

            return model;
        }
}

0

क्योंकि एक ही काम करने के कम से कम दस तरीके होना हमेशा अच्छा होता है:

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Something {
    @RequestMapping("/path")
    public ModelAndView somethingPath() {
        return new ModelAndView("/", HttpStatus.NOT_FOUND);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.