Trigger 404 nel controller Spring-MVC?


194

Come posso ottenere un controller Spring 3.0 per attivare un 404?

Ho un controller con @RequestMapping(value = "/**", method = RequestMethod.GET)e per alcuni URL che accedono al controller, voglio che il contenitore produca un 404.

Risposte:


326

Dalla primavera 3.0 puoi anche lanciare un'eccezione dichiarata con @ResponseStatusannotazione:

@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
Interessante. Puoi specificare quale HttpStatus utilizzare nel sito di lancio (ovvero non averlo compilato nella classe Exception)?
matt b

1
@mattb: Penso che il punto @ResponseStatussia che tu definisca un intero gruppo di classi di eccezioni ben tipizzate e ben denominate, ognuna con la propria @ResponseStatus. In questo modo, si disaccoppia il codice del controller dai dettagli dei codici di stato HTTP.
Skaffman,

10
Questo può essere esteso per supportare la restituzione di un corpo che contiene più descrizione dell'errore?
Tom,

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

6
Se si utilizza questa eccezione ResourceNotFound solo per il controllo del flusso, è forse consigliabile eseguire l'override ResourceNotFound.fillInStackTrace()con un'implementazione vuota.
Ralph


36

Riscrivi la firma del tuo metodo in modo che accetti HttpServletResponsecome parametro, in modo da poterlo chiamare setStatus(int).

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


8
Questa è l'unica risposta corretta se qualcuno è alla ricerca di un modo per dire al richiedente http di aver fatto un errore senza inondare il team di produttori con una serie di eccezioni che non possono risolvere.
Alex R

4
setStatus(int)javadoc afferma quanto segue: Se si utilizza questo metodo per impostare un codice di errore, il meccanismo della pagina di errore del contenitore non verrà attivato. Se si verifica un errore e il chiamante desidera richiamare una pagina di errore definita nell'applicazione Web, è sendErrornecessario utilizzare invece.
Philippe Gioseffi,

@AlexR Le eccezioni gestite non devono inondare il team operativo. In tal caso, la registrazione viene eseguita in modo errato.
Raedwald,

25

Vorrei menzionare che esiste un'eccezione (non solo) per 404 di default fornita da Spring. Vedere la documentazione di primavera per i dettagli. Quindi, se non hai bisogno della tua eccezione, puoi semplicemente farlo:

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

    ...

  }

11
Questo sembra essere pensato per un caso specifico, quando Spring non riesce a trovare un gestore. Il caso in questione è quando Spring riesce a trovare un gestore, ma l'utente vuole restituire un 404 per un altro motivo.
Roy Truelove,

2
Lo sto usando quando il mio ulr mapping per il metodo handler è dinamico. Quando l'entità non esiste in base al @PathVariablemio punto di vista, non è possibile gestire le richieste. Pensi che sia meglio / più pulito usare la tua eccezione annotata @ResponseStatus(value = HttpStatus.NOT_FOUND) ?
michal.kreuzman,

1
Nel tuo caso suona bene, ma non so che consiglierei le eccezioni trovate nel link che hai fornito per gestire tutti i casi in cui è necessaria un'eccezione - a volte dovresti crearne uno tuo.
Roy Truelove,

Bene, Spring ha fornito un'eccezione e una solo per 404. Avrebbero dovuto chiamarla 404Exception o crearne una. Ma com'è ora, penso che sia ok lanciarlo ogni volta che hai bisogno di un 404.
Autra,

Bene, tecnicamente parlando va bene - invierai 404 header di stato. Ma il messaggio di errore automatico - contenuto della risposta - è "Nessun metodo di gestione delle richieste con nome ..." che probabilmente non è qualcosa che vuoi mostrare all'utente.
Olli

24

Dalla primavera 3.0.2 è possibile restituire ResponseEntity <T> come risultato del metodo del controller:

@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> è più flessibile dell'annotazione @ResponseBody - vedi un'altra domanda )


2
ovviamente flessibile ma sconfigge i vantaggi della programmazione dichiarativa
rohanagarwal,

3
Se stai usando Sentry o simili in PROD e non vuoi spammarlo con errori che non sono errori reali, questa soluzione è molto meglio rispetto a quella che usa eccezioni per questa situazione non eccezionale.
Tobias Hermann,

Non dimenticare come popolare il corpo (con l'oggetto reale). esempio "Object" generico: Object returnItemBody = new Object (); return ResponseEntity.status (HttpStatus.OK) .body (returnItemBody);
granadaCoder

16

è possibile utilizzare @ControllerAdvice per gestire le eccezioni. Il comportamento predefinito della classe annotata @ControllerAdvice aiuterà tutti i controller noti.

quindi verrà chiamato quando qualsiasi controller che hai genera 404 errore.

come il seguente:

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

e mappa questo errore di risposta 404 nel tuo web.xml, come il seguente:

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

Spero che aiuti .


2
sono state mappate le eccezioni di tipo Exception (e sottoclassi) con un codice di stato 404. Hai mai pensato che ci siano errori del server interno? Come pensi di gestirli in GlobalControllerExceptionHandler?
rohanagarwal,

Questo NON ha funzionato per i controller REST, restituisce una risposta vuota.
Rustyx,

10

Se il tuo metodo controller è per qualcosa come la gestione dei file, allora ResponseEntityè molto utile:

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

9

Mentre la risposta contrassegnata è corretta, esiste un modo per raggiungere questo obiettivo senza eccezioni. Il servizio sta restituendo Optional<T>l'oggetto cercato e questo viene mappato su HttpStatus.OKse trovato e su 404 se vuoto.

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

}

Mi piace questo approccio in generale, ma l'uso di Optionals a volte finisce per essere un anti-pattern. E diventa complicato quando si restituiscono raccolte.
jfzr,

7

Consiglierei di lanciare HttpClientErrorException , in questo modo

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

È necessario ricordare che ciò può essere fatto solo prima che qualsiasi cosa venga scritta nel flusso di output servlet.


4
Tale eccezione viene generata dal client HTTP Spring. Spring MVC sembra non riconoscere questa eccezione. Quale versione di primavera stai usando? Stai ricevendo un 404 con quell'eccezione?
Eduardo,

1
Questo fa tornare Spring Boot:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
slim

Questa è un'eccezione per un client HTTP, non per un controller. Quindi utilizzarlo nel contesto specificato non è appropriato.
Alexey,

3

Questo è un po 'tardi, ma se stai usando Spring Data REST allora c'è già org.springframework.data.rest.webmvc.ResourceNotFoundException Usa anche l' @ResponseStatusannotazione. Non è più necessario creare un'eccezione di runtime personalizzata.


2

Inoltre, se si desidera restituire lo stato 404 dal controller, è sufficiente eseguire questa operazione

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

In questo modo riceverai un errore 404 nel caso in cui desideri restituire un 404 dal tuo controller.


0

Semplicemente puoi usare web.xml per aggiungere il codice di errore e la pagina di errore 404. Ma assicurati che la pagina di errore 404 non debba essere localizzata in WEB-INF.

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

Questo è il modo più semplice per farlo, ma questo ha alcune limitazioni. Supponiamo che tu voglia aggiungere lo stesso stile per questa pagina che hai aggiunto altre pagine. In questo modo non puoi farlo. Devi usare il@ResponseStatus(value = HttpStatus.NOT_FOUND)


Questo è il modo di farlo, ma considera con esso HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;dal codice del controller. Ora dall'esterno la risposta non sembra diversa da un normale 404 che non ha colpito nessun controller.
Darryl Miles,

questo non innesca un 404, lo gestisce solo se succede
Alex R

0

Configura web.xml con impostazione

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

Crea nuovo controller

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

Perché è sempre bene avere almeno dieci modi di fare la stessa cosa:

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);
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.