102

I have a REST endpoint implemented with Spring MVC @RestController. Sometime, depends on input parameters in my controller I need to send http redirect on client.

Is it possible with Spring MVC @RestController and if so, could you please show an example ?

alexanoid
  • 24,051
  • 54
  • 210
  • 410
  • 2
    http://stackoverflow.com/questions/9311940/redirect-to-dynamic-url-in-spring-mvc – kamokaze Mar 16 '15 at 19:29
  • 2
    Your linked post is NOT for ```@RestController``` classes because it includes ```@ResponseBody```. Your "redirect:" string would not be interpreted as a view. – Jan Mar 31 '15 at 15:41

6 Answers6

153

Add an HttpServletResponse parameter to your Handler Method then call response.sendRedirect("some-url");

Something like:

@RestController
public class FooController {

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

}
Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
Neil McGuigan
  • 46,580
  • 12
  • 123
  • 152
  • 4
    Sadly this looks like the only solution. I was also hoping there is a nicer way without a HttpServletResponse parameter. – Jan Mar 31 '15 at 15:40
  • 2
    @MajidLaissi - actually, it is quite sad. For the most part, it is possible to entirely abstract Spring MVC controllers away from any dependency on HTTP as a transport protocol at all, but that is unfortunately not possible here. – Jules Aug 03 '17 at 13:11
  • I like both this solution and the one by @Arne Burmeister but neither of them solve my problem of browser not handling the redirect (HTTP 302) of a XHR request's response because of reasons mentioned [here](https://stackoverflow.com/questions/228225/prevent-redirection-of-xmlhttprequest) – Mahesh Mar 08 '18 at 11:38
  • This is `302`, how about return `301` ? – Eric Jun 23 '19 at 07:41
  • there is, instead of a `@RestController` use a `@Controller` and a method returning a `String` as your template f.i. `return "redirect:/mytemplate"` – Phill Alexakis Apr 11 '20 at 09:52
  • @PhillAlexakis, sometimes it could be app without configured ViewResolver (rest api, for example) and in that case you could not use redirect in such way – Dmitriy Oct 14 '21 at 14:11
  • https://stackoverflow.com/a/71886060/6637228 – Marek Bernád Sep 13 '22 at 07:49
86

To avoid any direct dependency on HttpServletRequest or HttpServletResponse I suggest a "pure Spring" implementation returning a ResponseEntity like this:

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

If your method always returns a redirect, use ResponseEntity<Void>, otherwise whatever is returned normally as generic type.

Arne Burmeister
  • 20,046
  • 8
  • 53
  • 94
  • 22
    Or in one line `return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, newUrl).build();` – Johannes Flügel Jan 25 '18 at 15:44
  • 1
    Does newUrl in this case have to be a fully qualified URL? If so, this solution would be clunky at best when redirecting to another URL in your app, because you would have to build the entire URL. Using HttpServletResponse allows you to send a redirect relative to the servlet container root. – matt forsythe Apr 02 '19 at 19:10
  • and it works like a charm if you use swagger client generation code ;-) – Jimmy Pannier Nov 08 '19 at 06:48
  • I am trying to add a custom header in this along with "location", but its not working any idea? @Arne – Arpit Suthar May 03 '20 at 09:29
26

Came across this question and was surprised that no-one mentioned RedirectView. I have just tested it, and you can solve this in a clean 100% spring way with:

@RestController
public class FooController {

    @RequestMapping("/foo")
    public RedirectView handleFoo() {
        return new RedirectView("some-url");
    }
}
DhatGuy
  • 381
  • 3
  • 5
  • Any idea how to set the url with the context path? – Bahij.Mik Apr 15 '20 at 10:32
  • @Bahij.Mik I'm assuming you're asking about the servlet context path -- the following tells me that you can autowire the servlet context and retrieve the path from it [link](https://stackoverflow.com/questions/12236590/retrieving-the-servlet-context-path-from-a-spring-web-application#15084082) – DhatGuy Apr 23 '20 at 09:36
3

redirect means http code 302, which means Found in springMVC.

Here is an util method, which could be placed in some kind of BaseController:

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

But sometimes might want to return http code 301 instead, which means moved permanently.

In that case, here is the util method:

protected ResponseEntity movedPermanently(HttpServletResponse response, String url) { // 301, moved permanently,
    return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, url).build();
}
Eric
  • 22,183
  • 20
  • 145
  • 196
0

As the redirections are usually needed in a not-straightforward path, I think throwing an exception and handling it later is my favourite solution.

Using a 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
    );
  }
}

The exception class in my case:

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

And I trigger it like this:

if (null == remoteUser)
  throw new NotLoggedInException("please log in", LOGIN_URL);
Árpád Magosányi
  • 1,394
  • 2
  • 19
  • 35
-12

if you @RestController returns an String you can use something like this

return "redirect:/other/controller/";

and this kind of redirect is only for GET request, if you want to use other type of request use HttpServletResponse

skuarch
  • 115
  • 1
  • 3
  • 17
    It will work only for `@Controller` but not for the `@RestController` which is a specialized version of `@Controller` and add `@ResponseBody` annotation. Spring Framework will convert the return value and write it to the http response automatically. `@RestController public class RedirectController { @RequestMapping("/redirect") public String redirect() { return "redirect:/other/controller/"; } }` and if we will try to access that url `curl localhost:8080/redirect` we will simply see `redirect:/other/controller/` string as result. – Anton Balaniuc Nov 04 '16 at 13:36
  • 4
    Anton Balaniuc is right, and reading your whole answer still is not right. In a RestController it will just return the String "redirect:/other/controller", it will not redirect – S. Jacob Powell Aug 17 '17 at 06:32