MVC @RestController springen und umleiten

74

Ich habe einen REST-Endpunkt mit Spring MVC @RestController implementiert. Abhängig von den Eingabeparametern in meinem Controller muss ich manchmal eine http-Umleitung auf dem Client senden.

Ist es mit Spring MVC @RestController möglich und wenn ja, können Sie bitte ein Beispiel zeigen?

alexanoid
quelle
2
Ihr verlinkter Beitrag ist NICHT für @RestControllerKlassen, da er enthält @ResponseBody. Ihre Zeichenfolge "Redirect:" wird nicht als Ansicht interpretiert.
Januar

Antworten:

117

Fügen Sie HttpServletResponseIhrer Handler-Methode einen Parameter hinzu und rufen Sie ihn aufresponse.sendRedirect("some-url");

Etwas wie:

@RestController
public class FooController {

  @RequestMapping("/foo")
  void handleFoo(HttpServletResponse response) throws IOException {
    response.sendRedirect("some-url");
  }

}
Neil McGuigan
quelle
3
Leider scheint dies die einzige Lösung zu sein. Ich hatte auch gehofft, dass es einen schöneren Weg ohne einen HttpServletResponse-Parameter gibt.
Januar
2
@MajidLaissi - eigentlich ist es ziemlich traurig. Zum größten Teil ist es möglich, Spring MVC-Controller vollständig von jeglicher Abhängigkeit von HTTP als Transportprotokoll zu abstrahieren, aber das ist hier leider nicht möglich.
Jules
Ich mag sowohl diese Lösung als auch die von @Arne Burmeister, aber keiner von beiden löst mein Problem, dass der Browser die Umleitung (HTTP 302) der Antwort einer XHR-Anfrage aus den hier
Mahesh,
Dies ist 302, wie etwa Rückkehr 301?
Eric Wang
Es gibt anstelle einer @RestControllerVerwendung eine @Controllerund eine Methode, die a Stringals Vorlage zurückgibtreturn "redirect:/mytemplate"
Phill Alexakis
56

Um eine direkte Abhängigkeit von einer "reinen Spring" -Implementierung zu vermeiden HttpServletRequestoder diese HttpServletResponsevorzuschlagen, wird eine ResponseEntity wie folgt zurückgegeben:

HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(newUrl));
return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY);

Wenn Ihre Methode immer eine Umleitung zurückgibt, verwenden Sie ResponseEntity<Void>, andernfalls wird alles, was normalerweise als generischer Typ zurückgegeben wird, verwendet.

Arne Burmeister
quelle
10
Oder in einer Zeilereturn ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, newUrl).build();
Johannes Flügel
1
Muss newUrl in diesem Fall eine vollständig qualifizierte URL sein? In diesem Fall wäre diese Lösung bestenfalls umständlich, wenn Sie zu einer anderen URL in Ihrer App umleiten, da Sie die gesamte URL erstellen müssten. Mit HttpServletResponse können Sie eine Umleitung relativ zum Servlet-Container-Stamm senden.
Matt Forsythe
und es funktioniert wie ein Zauber, wenn Sie Swagger-Client-Generierungscode verwenden ;-)
Jimmy Pannier
Ich versuche, einen benutzerdefinierten Header zusammen mit "Speicherort" hinzuzufügen, aber es funktioniert keine Idee? @Arne
Arpit Suthar
Eine elegante Art, es zu tun, danke für das Teilen.
Mohammed Salah
18

Kam über diese Frage und war überrascht, dass niemand RedirectView erwähnte. Ich habe es gerade getestet, und Sie können dies auf saubere, 100% ige Art und Weise lösen mit:

@RestController
public class FooController {

    @RequestMapping("/foo")
    public RedirectView handleFoo() {
        return new RedirectView("some-url");
    }
}
DhatGuy
quelle
Irgendeine Idee, wie man die URL mit dem Kontextpfad festlegt?
Bahij.Mik
@ Bahij.Mik Ich gehe davon aus, dass Sie nach dem Servlet-Kontextpfad fragen - im Folgenden erfahren Sie, dass Sie den Servlet-Kontext automatisch verdrahten und den Pfad von diesem Link
abrufen können
1

redirectbedeutet http-Code 302, was Foundin springMVC bedeutet .

Hier ist eine util-Methode, die in eine Art von platziert werden könnte BaseController:

protected ResponseEntity found(HttpServletResponse response, String url) throws IOException { // 302, found, redirect,
    response.sendRedirect(url);
    return null;
}

Aber manchmal möchten Sie vielleicht 301stattdessen http-Code zurückgeben , was bedeutet moved permanently.

In diesem Fall ist hier die util-Methode:

protected ResponseEntity movedPermanently(HttpServletResponse response, String url) { // 301, moved permanently,
    return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, url).build();
}
Eric Wang
quelle
0

Da die Umleitungen normalerweise auf einem nicht einfachen Weg benötigt werden, halte ich es für meine Lieblingslösung, eine Ausnahme auszulösen und später zu behandeln.

Verwenden eines ControllerAdvice

@ControllerAdvice
public class RestResponseEntityExceptionHandler
    extends ResponseEntityExceptionHandler {

  @ExceptionHandler(value = {
      NotLoggedInException.class
  })
  protected ResponseEntity<Object> handleNotLoggedIn(
      final NotLoggedInException ex, final WebRequest request
  ) {
    final String bodyOfResponse = ex.getMessage();

    final HttpHeaders headers = new HttpHeaders();
    headers.add("Location", ex.getRedirectUri());
    return handleExceptionInternal(
        ex, bodyOfResponse,
        headers, HttpStatus.FOUND, request
    );
  }
}

Die Ausnahmeklasse in meinem Fall:

@Getter
public class NotLoggedInException extends RuntimeException {

  private static final long serialVersionUID = -4900004519786666447L;

  String redirectUri;

  public NotLoggedInException(final String message, final String uri) {
    super(message);
    redirectUri = uri;
  }
}

Und ich löse es so aus:

if (null == remoteUser)
  throw new NotLoggedInException("please log in", LOGIN_URL);
Árpád Magosányi
quelle
-12

Wenn Sie @RestController einen String zurückgeben , können Sie so etwas verwenden

return "redirect:/other/controller/";

Diese Art der Umleitung gilt nur für GET- Anforderungen. Wenn Sie einen anderen Anforderungstyp verwenden möchten, verwenden Sie HttpServletResponse

Skuarch
quelle
14
Es funktioniert nur für @Controllernicht aber für die , @RestControllerdie eine spezielle Version von ist @Controllerund Add @ResponseBodyAnnotation. Spring Framework konvertiert den Rückgabewert und schreibt ihn automatisch in die http-Antwort. @RestController public class RedirectController { @RequestMapping("/redirect") public String redirect() { return "redirect:/other/controller/"; } } und wenn wir versuchen, auf diese URL zuzugreifen, sehen curl localhost:8080/redirect wir einfach einen redirect:/other/controller/String als Ergebnis.
Anton Balaniuc
3
Anton Balaniuc hat recht, und das Lesen Ihrer gesamten Antwort ist immer noch nicht richtig. In einem RestController wird nur der String "redirect: / other / controller" zurückgegeben, es wird nicht umgeleitet
S. Jacob Powell