2010-01-14 5 views
154

Comment puis-je obtenir un contrôleur Spring 3.0 pour déclencher un 404?Trigger 404 dans le contrôleur Spring-MVC?

J'ai un contrôleur avec @RequestMapping(value = "/**", method = RequestMethod.GET) et pour certains URLs accéder au contrôleur, je veux le conteneur à venir avec un 404.

Répondre

270

Depuis le printemps 3.0, vous pouvez également lancer une exception déclarée avec @ResponseStatus annotation:

@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

Intéressant. Pouvez-vous spécifier quel HttpStatus utiliser sur le site de lancement (c'est-à-dire ne pas l'avoir compilé dans la classe Exception)? –

+1

@mattb: Je pense que le point de '@ ResponseStatus' est que vous définissez tout un tas de classes d'exceptions bien typées et bien nommées, chacune avec leur propre' @ ResponseStatus'. De cette façon, vous découplerez votre code de contrôleur du détail des codes de statut HTTP. – skaffman

+0

Je vois. Jolie idée soignée. Je dois commencer à utiliser les annotations 3.0 et apprendre toute cette bonté! –

29

Ressaisissez votre signature de la méthode afin qu'il accepte HttpServletResponse comme paramètre, afin que vous puissiez appelez setStatus(int) à ce sujet.

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

+3

C'est la seule bonne réponse si quelqu'un cherche un moyen de dire au demandeur http qu'il a fait une erreur sans submerger l'équipe des produits avec un tas d'exceptions qu'il ne peut pas résoudre. –

+1

'setStatus (int)' javadoc se lit comme suit: Si cette méthode est utilisée pour définir un code d'erreur, le mécanisme de page d'erreur du conteneur ne sera pas déclenché. S'il y a une erreur et que l'appelant souhaite invoquer une page d'erreur définie dans l'application Web, alors 'sendError' doit être utilisé à la place. –

23

Je voudrais mentionner qu'il ya exception (non seulement) pour 404 par défaut fourni par Spring. Voir Spring documentation pour plus de détails. Donc, si vous n'avez pas besoin de votre propre exception, vous pouvez simplement faire ceci:

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

    ... 

    } 
+9

Ceci semble être destiné à un cas spécifique - lorsque Spring ne trouve pas de gestionnaire. Le cas en question est lorsque Spring * peut * trouver un gestionnaire, mais l'utilisateur veut retourner un 404 pour une autre raison. –

+2

Je l'utilise lorsque ma méthode de mapping ulr pour handler est dynamique. Quand l'entité n'existe pas basée sur '@ PathVariable', il n'y a pas de gestion des requêtes de mon point de vue. Pensez-vous qu'il est préférable d'utiliser votre propre exception annotée avec '@ResponseStatus (value = HttpStatus.NOT_FOUND) '? –

+1

Dans votre cas, cela semble bien, mais je ne sais pas que je recommanderais les exceptions trouvées dans le lien que vous avez fourni pour gérer tous les cas où une exception est nécessaire - parfois, vous devriez faire votre propre. –

0

simplement vous pouvez utiliser web.xml pour ajouter du code d'erreur 404 et page d'erreur. Mais assurez-vous que la page d'erreur 404 ne doit pas se trouver sous WEB-INF.

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

Ceci est la façon la plus simple de le faire, mais cela a certaines limites. Supposons que vous vouliez ajouter le même style pour cette page que vous avez ajouté d'autres pages. De cette façon, vous ne pouvez pas à cela. Vous devez utiliser le @ResponseStatus(value = HttpStatus.NOT_FOUND)

+0

Ceci est la manière de le faire, mais considérez-le comme suit: 'HttpServletResponse # sendError (HttpServletResponse.SC_NOT_FOUND); return null; 'à partir du code du contrôleur. Maintenant, de l'extérieur, la réponse ne ressemble pas à un 404 normal qui n'a touché aucun contrôleur. –

+0

cela ne déclenche pas un 404, il suffit de le gérer si un –

8

Si votre méthode de commande est pour quelque chose comme le traitement des fichiers puis ResponseEntity est très pratique:

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

Configurer web.xml avec réglage

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

Créer un nouveau contrôleur

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

vous pouvez utiliser le @ControllerAdvice pour gérer vos exceptions, Le comportement par défaut de la classe annotée @ControllerAdvice aidera tous les contrôleurs connus.

donc il sera appelé quand un contrôleur vous jette erreur 404.

comme ce qui suit:

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

et la carte cette erreur 404 de réponse dans votre web.xml, comme ce qui suit:

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

Hope qui aide.

+2

vous avez mappé des exceptions de type Exception (et sous-classes) avec un code d'état 404. Avez-vous déjà pensé qu'il y a des erreurs de serveur interne? Comment envisagez-vous de gérer ceux dans votre GlobalControllerExceptionHandler? – rohanagarwal

+0

Cela n'a pas fonctionné pour les contrôleurs REST, renvoie une réponse vide. – rustyx

13

Depuis le printemps 3.0.2 vous pouvez revenir ResponseEntity<T> à la suite de la méthode du contrôleur:

@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> est plus souple que l'annotation @ResponseBody - voir another question)

+0

flexible bien sûr, mais vainc les avantages de la programmation déclarative – rohanagarwal

4

Je recommande de jeter HttpClientErrorException, comme ceci

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

Vous devez vous rappeler que ceci peut être fait seulement avant que quelque chose ne soit écrit dans le servlet flux de sortie.

+2

Cette exception est levée par le client HTTP Spring. Spring MVC ne semble pas reconnaître cette exception. Quelle version de Spring utilisez-vous? Est-ce que vous obtenez un 404 avec cette exception? – Eduardo

+0

Cela provoque le retour de Spring Boot: 'Whitelabel Error Page \ n .... \ n Une erreur inattendue s'est produite (type = erreur interne du serveur, état = 500). \ n 404 Ceci est votre erreur introuvable – slim

+0

Ceci est une exception pour un client HTTP, pas pour un contrôleur. Donc, l'utiliser dans le contexte spécifié est inapproprié. – Alexey

5

Bien que la réponse marquée soit correcte, il existe un moyen d'y parvenir sans exceptions. Le service renvoie Optional<T> de l'objet recherché et celui-ci est mappé à HttpStatus.OK si trouvé et à 404 si vide.

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

} 
1

Aussi, si vous souhaitez revenir statut 404 de votre contrôleur tout ce que vous avez besoin est de le faire

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

En faisant cela, vous recevrez une erreur 404 dans le cas où vous voulez retourner un 404 de votre contrôleur.

Questions connexes