Spring-MVC kontrol cihazında 404 tetikleyicisi?


194

404'ü tetiklemek için bir Spring 3.0 kontrol cihazını nasıl edinebilirim ?

Bir denetleyicim var @RequestMapping(value = "/**", method = RequestMethod.GET)ve denetleyiciye erişen bazı URL'ler için , kabın bir 404 ile gelmesini istiyorum.

Yanıtlar:


326

İlkbahar 3.0'dan bu yana, @ResponseStatusek açıklama ile bildirilen bir İstisna atayabilirsiniz:

@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
İlginç. Atış sitesinde hangi HttpStatus'un kullanılacağını belirtebilir misiniz (yani, Exception sınıfında derlenmemiş)?
mat b

1
@mattb: Sanırım asıl önemli olan @ResponseStatus, her biri kendine ait olan güçlü bir şekilde yazılmış, iyi adlandırılmış istisna sınıflarından oluşan bir grup tanımlamanızdır @ResponseStatus. Bu şekilde, denetleyici kodunuzu HTTP durum kodlarının ayrıntısından ayırırsınız.
skaffman

10
Bu, hata hakkında daha fazla açıklama içeren bir gövdeyi iade etmeyi desteklemek için genişletilebilir mi?
Tom

7
@Tom:@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
Nailgun

6
Bu ResourceNotFound özel durumunu yalnızca akış denetimi için kullanırsanız, ResourceNotFound.fillInStackTrace()boş bir uygulama ile geçersiz kılmak iyi bir fikir olabilir .
Ralph


36

Yöntem imzanızı HttpServletResponseparametre olarak kabul edileceği şekilde yeniden yazın , böylecesetStatus(int) onu .

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


8
Birisi http istemcisine dürtmek ops ekibini düzeltemedikleri bir grup istisna ile doldurmazken hata yaptığını söylemenin bir yolunu arıyorsa, tek doğru cevap budur.
Alex R

4
setStatus(int)javadoc şöyle ifade eder: Bu yöntem bir hata kodu ayarlamak için kullanılırsa, kabın hata sayfası mekanizması tetiklenmez. Bir hata varsa ve arayan kişi web uygulamasında tanımlanan bir hata sayfasını çağırmak istiyorsa, sendErrorbunun yerine kullanılmalıdır.
Philippe Gioseffi

@AlexR Ele alınan istisnalar ops ekibine su basmamalıdır. Eğer öyleyse, günlüğe kaydetme yanlış yapılıyor.
Raedwald

25

Spring tarafından varsayılan olarak 404 için (sadece) istisna olduğunu belirtmek isterim. Ayrıntılar için Yay belgelerine bakın. Yani kendi istisnanıza ihtiyacınız yoksa bunu yapabilirsiniz:

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

    ...

  }

11
Bu, Spring'in bir işleyici bulamadığı belirli bir durum için düşünülmüş görünüyor. Bahar zaman söz konusu olduğu için bir işleyici bulmak, ancak kullanıcı başka bir nedenle bir 404 dönmek istiyor.
Roy Truelove

2
Işleyici yöntemi için ulr eşlemem dinamik olduğunda kullanıyorum. Varlık esas alındığında @PathVariable, benim görüşüme göre herhangi bir istek işleme konulmaz. Ek açıklama eklenmiş kendi İstisnenizi kullanmanın daha iyi / daha temiz olduğunu düşünüyor musunuz @ResponseStatus(value = HttpStatus.NOT_FOUND) ?
michal.kreuzman

1
Sizin durumunuzda kulağa hoş geliyor, ancak bir istisnanın gerekli olduğu tüm durumları ele almak için sağladığınız bağlantıda bulunan istisnaları tavsiye edeceğimi bilmiyorum - bazen kendinizinkini yapmanız gerekir.
Roy Truelove

Spring, sadece 404 için bir istisna ve bir istisna sağladı. 404Exception adını vermeli veya bir tane oluşturmalıydılar. Ama şimdi olduğu gibi,
404'e

Teknik olarak sorun değil - 404 durum başlığı gönderirsiniz. Ancak otomatik hata mesajı - yanıt içeriği - muhtemelen kullanıcıya göstermek istediğiniz bir şey olmayan "adıyla istek işleme yöntemi yok ..." dır.
Olli

24

İlkbahar 3.0.2'den beri , denetleyicinin yönteminin bir sonucu olarak ResponseEntity <T> öğesini döndürebilirsiniz :

@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 ek açıklamasından daha esnektir - başka bir soruya bakın )


2
tabii ki esnek ama bildirimsel programlamanın faydalarını yendi
rohanagarwal

3
PROD'da Sentry veya benzeri bir şey kullanıyorsanız ve gerçek hata olmayan hatalarla spam yapmak istemiyorsanız, bu çözüm, bu istisnai olmayan durum için istisnalar kullanan çözümle karşılaştırıldığında çok daha iyidir.
Tobias Hermann

Vücudu nasıl dolduracağınızı unutmayın (gerçek nesnenizle). generic "Object" örneği: Object returnItemBody = new Object (); return ResponseEntity.status (HttpStatus.OK) .body (returnItemBody);
granadaCoder

16

@ControllerAdvice kullanabilirsiniz senin İstisnalar, @ControllerAdvice açıklamalı sınıf bilinen tüm Kontrolörleri yardımcı olacaktır varsayılan davranışını işlemek için.

bu yüzden herhangi bir Denetleyici 404 hatası attığında çağrılır.

aşağıdaki gibi:

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

ve web.xml dosyasında bu 404 yanıt hatasını aşağıdaki gibi eşleyin:

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

Umarım yardımcı olur .


2
Özel durum (ve alt sınıflar) özel durumlarını 404 durum koduyla eşlediniz. Hiç dahili sunucu hataları olduğunu düşündünüz mü? GlobalControllerExceptionHandler'ınızdakileri nasıl işlemeyi planlıyorsunuz?
rohanagarwal

Bu REST denetleyicileri için işe yaramadı, boş bir yanıt döndürür.
rustyx

10

Denetleyici yönteminiz dosya işleme gibi bir şey içinse ResponseEntityçok kullanışlıdır:

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

9

Belirgin cevap doğru olsa da, istisnasız bunu başarmanın bir yolu vardır. Hizmet Optional<T>aranan nesneyi döndürüyor ve bu, HttpStatus.OKbulunursa ve boşsa 404 ile eşleniyor.

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

}

Genel olarak bu yaklaşımı seviyorum, ancak Optionals kullanmak bazen bir anti-desen haline geliyor. Ve koleksiyonları döndürürken karmaşıklaşıyor.
jfzr

7

Ben HttpClientErrorException , bu gibi atma tavsiye ederim

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

Bunun yalnızca sunucu uygulaması çıkış akışına herhangi bir şey yazılmadan önce yapılabileceğini unutmayın.


4
Bu istisna Spring HTTP istemcisi tarafından atılır. Bahar MVC bu istisnayı tanımıyor gibi görünüyor. Hangi Bahar versiyonunu kullanıyorsunuz? Bu istisna dışında bir 404 elde ediyor musunuz?
Eduardo

1
Bu Spring Boot'un geri dönmesine neden olur:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
ince

Bu bir HTTP istemcisi için bir istisnadır, bir denetleyici için değil. Bu yüzden belirtilen bağlamda kullanmak uygun değildir.
Alexey

3

Bu biraz geç, ama Spring Data REST kullanıyorsanız o zaman zaten ek açıklama org.springframework.data.rest.webmvc.ResourceNotFoundException kullanır @ResponseStatus. Artık özel bir çalışma zamanı istisnası oluşturmanıza gerek yok.


2

Ayrıca 404 durumunu kontrol cihazınızdan döndürmek istiyorsanız, tek yapmanız gereken bunu yapmaktır

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

Bunu yaptığınızda, kontrol cihazınızdan bir 404 döndürmek istediğinizde bir 404 hatası alırsınız.


0

Basitçe hata kodu ve 404 hata sayfası eklemek için web.xml'yi kullanabilirsiniz. Ancak 404 hata sayfasının WEB-INF altında bulunmaması gerektiğinden emin olun.

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

Bunu yapmanın en basit yolu bu ama bazı sınırlamaları var. Bu sayfa için başka sayfalar eklediğiniz aynı stili eklemek isteyip istemediğinizi varsayalım. Bu şekilde bunu yapamazsınız. Kullanmak zorunda@ResponseStatus(value = HttpStatus.NOT_FOUND)


Bunu yapmanın yolu ama HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;denetleyici kodundan düşünün . Şimdi dışarıdan yanıt, herhangi bir denetleyiciye çarpmayan normal bir 404'ten farklı görünmüyor.
Darryl Miles

bu bir 404 tetiklemez, sadece bir tane olursa onu ele alır
Alex R

0

Web.xml dosyasını ayar ile yapılandırın

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

Yeni denetleyici oluştur

   /**
     * 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

Çünkü aynı şeyi yapmanın en az on yoluna sahip olmak her zaman iyidir:

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