Trigger 404 im Spring-MVC-Controller?

193

Wie kann ich einen Spring 3.0-Controller dazu bringen, einen 404 auszulösen?

Ich habe einen Controller mit @RequestMapping(value = "/**", method = RequestMethod.GET)und für einige URLs, die auf den Controller zugreifen, möchte ich, dass der Container einen 404 ausgibt.

N / A.
quelle

Antworten:

326

Seit Spring 3.0 können Sie auch eine mit @ResponseStatusAnmerkungen deklarierte Ausnahme auslösen:

@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(); 
        }
    }
}
axtavt
quelle
2
Interessant. Können Sie angeben, welcher HttpStatus an der Throw-Site verwendet werden soll (dh nicht in die Exception-Klasse kompiliert wurde)?
Matt B
1
@mattb: Ich denke, der Punkt @ResponseStatusist, dass Sie eine ganze Reihe von stark typisierten, gut benannten Ausnahmeklassen definieren, jede mit ihren eigenen @ResponseStatus. Auf diese Weise entkoppeln Sie Ihren Controller-Code von den Details der HTTP-Statuscodes.
Skaffman
9
Kann dies erweitert werden, um die Rückgabe eines Körpers mit mehr Beschreibung des Fehlers zu unterstützen?
Tom
7
@ Tom:@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
Nailgun
6
Wenn Sie diese ResourceNotFound-Ausnahme nur für die Flusssteuerung verwenden, ist es möglicherweise eine gute Idee, sie ResourceNotFound.fillInStackTrace()mit einer leeren Implementierung zu überschreiben .
Ralph
36

Schreiben Sie Ihre Methodensignatur so um, dass sie HttpServletResponseals Parameter akzeptiert wird, damit Sie sie aufrufen setStatus(int)können.

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

matt b
quelle
8
Dies ist die einzig richtige Antwort, wenn jemand nach einer Möglichkeit sucht, dem http-Anforderer mitzuteilen, dass er einen Fehler gemacht hat, ohne das Produktteam mit einer Reihe von Ausnahmen zu überfluten, die er nicht beheben kann.
Alex R
4
setStatus(int)javadoc gibt Folgendes an: Wenn diese Methode zum Festlegen eines Fehlercodes verwendet wird, wird der Fehlerseitenmechanismus des Containers nicht ausgelöst. Wenn ein Fehler vorliegt und der Anrufer eine in der Webanwendung definierte Fehlerseite aufrufen möchte, sendErrormuss diese stattdessen verwendet werden.
Philippe Gioseffi
@AlexR Behandelte Ausnahmen sollten das Ops-Team nicht überfluten. Wenn dies der Fall ist, wird die Protokollierung falsch durchgeführt.
Raedwald
25

Ich möchte erwähnen, dass es standardmäßig eine Ausnahme (nicht nur) für 404 gibt, die von Spring bereitgestellt wird. Weitere Informationen finden Sie in der Spring-Dokumentation . Wenn Sie also keine eigene Ausnahme benötigen, können Sie dies einfach tun:

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

    ...

  }
michal.kreuzman
quelle
11
Dies scheint für einen bestimmten Fall gedacht zu sein - wenn Spring keinen Handler finden kann. Der Fall in Frage, wenn der Frühling kann einen Handler finden, aber der Benutzer ein 404 aus einem anderen Grunde zurückzukehren.
Roy Truelove
2
Ich verwende es, wenn meine ulr-Zuordnung für die Handler-Methode dynamisch ist. Wenn eine Entität basierend auf nicht existiert, @PathVariablegibt es aus meiner Sicht keine Anforderungsbearbeitung. Denken Sie, dass es besser / sauberer ist, eine eigene Ausnahme zu verwenden, die mit Anmerkungen versehen ist @ResponseStatus(value = HttpStatus.NOT_FOUND) ?
michal.kreuzman
1
In Ihrem Fall klingt es gut, aber ich weiß nicht, dass ich die Ausnahmen in dem von Ihnen angegebenen Link empfehlen würde, um alle Fälle zu behandeln, in denen eine Ausnahme erforderlich ist - manchmal sollten Sie Ihre eigene machen.
Roy Truelove
Nun, Spring hat eine Ausnahme und eine nur für 404 bereitgestellt. Sie hätten sie 404Exception nennen oder eine erstellen sollen. Aber wie es jetzt ist, denke ich, dass es in Ordnung ist, dies zu werfen, wann immer Sie eine 404 benötigen.
autra
Technisch gesehen ist es in Ordnung - Sie senden den 404-Status-Header. Die automatische Fehlermeldung - Antwortinhalt - lautet jedoch "Keine Methode zur Bearbeitung von Anforderungen mit Namen ...". Dies möchten Sie dem Benutzer wahrscheinlich nicht anzeigen.
Olli
24

Seit Spring 3.0.2 können Sie ResponseEntity <T> als Ergebnis der Controller-Methode zurückgeben:

@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> ist flexibler als die @ ResponseBody-Annotation - siehe eine andere Frage )

Lu55
quelle
2
Natürlich flexibel, aber besiegt die Vorteile der deklarativen Programmierung
Rohanagarwal
3
Wenn Sie Sentry oder ähnliches in PROD verwenden und es nicht mit Fehlern spammen möchten, die keine tatsächlichen Fehler sind, ist diese Lösung viel besser als die, die Ausnahmen für diese nicht außergewöhnliche Situation verwendet.
Tobias Hermann
Vergessen Sie nicht, wie Sie den Körper (mit Ihrem eigentlichen Objekt) bevölkern. generisches "Objekt" -Beispiel: Objekt returnItemBody = new Object (); return ResponseEntity.status (HttpStatus.OK) .body (returnItemBody);
GranadaCoder
16

Sie können das @ControllerAdvice verwenden , um Ihre Ausnahmen zu behandeln. Das Standardverhalten der mit @ControllerAdvice kommentierten Klasse unterstützt alle bekannten Controller.

Daher wird es aufgerufen, wenn ein Controller, den Sie haben, einen 404-Fehler auslöst.

wie folgt:

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

und ordnen Sie diesen 404-Antwortfehler in Ihrer web.xml wie folgt zu:

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

Hoffentlich hilft das .


quelle
2
Sie haben Ausnahmen vom Typ Ausnahme (und Unterklassen) mit einem 404-Statuscode zugeordnet. Haben Sie jemals gedacht, dass es interne Serverfehler gibt? Wie wollen Sie mit denen in Ihrem GlobalControllerExceptionHandler umgehen?
Rohanagarwal
Dies funktionierte NICHT für REST-Controller, gibt eine leere Antwort zurück.
Rustyx
10

Wenn Ihre Controller-Methode für die Dateiverwaltung vorgesehen ist, ResponseEntityist dies sehr praktisch:

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

Obwohl die markierte Antwort korrekt ist, gibt es eine Möglichkeit, dies ausnahmslos zu erreichen. Der Dienst gibt Optional<T>das gesuchte Objekt zurück und dieses wird zugeordnet, HttpStatus.OKwenn es gefunden wird, und 404, wenn es leer ist.

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

}
user1121883
quelle
Ich mag diesen Ansatz im Allgemeinen, aber die Verwendung von Optionals ist manchmal ein Anti-Pattern. Und wird kompliziert, wenn Sammlungen zurückgegeben werden.
jfzr
7

Ich würde empfehlen, HttpClientErrorException wie folgt auszulösen

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

Sie müssen sich daran erinnern, dass dies nur möglich ist, bevor etwas in den Servlet-Ausgabestream geschrieben wird.

mmatczuk
quelle
4
Diese Ausnahme wird vom Spring HTTP-Client ausgelöst. Spring MVC scheint diese Ausnahme nicht zu erkennen. Welche Spring-Version verwenden Sie? Bekommst du mit dieser Ausnahme einen 404?
Eduardo
1
Dies führt dazu, dass Spring Boot zurückkehrt:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
schlank am
Dies ist eine Ausnahme für einen HTTP-Client, nicht für einen Controller. Die Verwendung im angegebenen Kontext ist daher unangemessen.
Alexey
3

Dies ist etwas spät, aber wenn Sie Spring Data REST verwenden, gibt es bereits org.springframework.data.rest.webmvc.ResourceNotFoundException eine @ResponseStatusAnnotation. Es ist nicht mehr erforderlich, eine benutzerdefinierte Laufzeitausnahme zu erstellen.

Pilot
quelle
2

Auch wenn Sie den 404-Status von Ihrem Controller zurückgeben möchten, müssen Sie dies nur tun

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

Auf diese Weise erhalten Sie einen 404-Fehler, falls Sie einen 404 von Ihrem Controller zurückgeben möchten.

AbdusSalam
quelle
0

Sie können einfach web.xml verwenden, um Fehlercode und 404-Fehlerseite hinzuzufügen. Stellen Sie jedoch sicher, dass sich die 404-Fehlerseite nicht unter WEB-INF befindet.

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

Dies ist der einfachste Weg, dies hat jedoch einige Einschränkungen. Angenommen, Sie möchten für diese Seite denselben Stil hinzufügen, den Sie für andere Seiten hinzugefügt haben. Auf diese Weise können Sie nicht dazu. Sie müssen die verwenden@ResponseStatus(value = HttpStatus.NOT_FOUND)

Rajith Delantha
quelle
Dies ist der Weg, dies zu tun, aber berücksichtigen Sie dies HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;anhand des Controller-Codes. Von außen sieht die Reaktion nicht anders aus als bei einem normalen 404, der keinen Controller getroffen hat.
Darryl Miles
Dies löst keinen 404 aus, sondern behandelt ihn nur, wenn einer passiert
Alex R
0

Konfigurieren Sie web.xml mit der Einstellung

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

Erstellen Sie einen neuen 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;
        }
}
Atish Narlawar
quelle
0

Weil es immer gut ist, mindestens zehn Möglichkeiten zu haben, dasselbe zu tun:

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