Spring MVC @RestController和重定向

104

我有一个使用Spring MVC @RestController实现的REST端点。有时,根据控制器中的输入参数,我需要向客户端发送http重定向。

请问使用Spring MVC @RestController是否可以实现此功能?如果可以,请给出一个示例。


2
https://dev59.com/dGox5IYBdhLWcg3wSCVW - kamokaze
2
你所链接的帖子不适用于@RestController类,因为它包含了@ResponseBody。你的"redirect:"字符串将不会被解释为视图。 - Jan
6个回答

157

在您的处理器方法中添加一个HttpServletResponse参数,然后调用response.sendRedirect("some-url");

类似这样:

@RestController
public class FooController {

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

}

4
很遗憾,这似乎是唯一的解决方案。我也希望有一种更好的方法,而不需要使用 HttpServletResponse 参数。 - Jan
2
@MajidLaissi - 实际上,这很令人难过。在大多数情况下,完全可以将Spring MVC控制器与HTTP作为传输协议的任何依赖关系完全抽象化,但不幸的是,在这种情况下不可能。 - Jules
我喜欢这个解决方案和@Arne Burmeister的解决方案,但它们都不能解决我的问题,即浏览器无法处理XHR请求响应的重定向(HTTP 302),因为原因在此处提到:https://dev59.com/dXVC5IYBdhLWcg3woSxW。 - Mahesh
@PhillAlexakis,有时可能是没有配置ViewResolver的应用程序(例如rest api),在这种情况下,您不能以这种方式使用重定向。 - Dmitriy
https://dev59.com/e7f4oIgBc1ULPQZFCYSW#71886060 - Marek Bernád
显示剩余2条评论

89

为了避免直接依赖于HttpServletRequestHttpServletResponse,我建议使用“纯Spring”实现,返回一个ResponseEntity,像这样:

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

如果你的方法总是返回一个重定向,使用 ResponseEntity<Void>,否则就按照通常的泛型类型返回。


24
返回已翻译的文本:return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, newUrl).build(); - Johannes Flügel
1
在这种情况下,newUrl是否必须是完全限定的URL?如果是这样,当重定向到应用程序中的另一个URL时,此解决方案最多会显得笨拙,因为您必须构建整个URL。使用HttpServletResponse允许您相对于servlet容器根目录发送重定向。 - matt forsythe
如果您使用Swagger客户端生成代码,它会像魔法一样正常工作 ;-) - Jimmy Pannier
我正在尝试在这个中添加一个自定义标题,以及“位置”,但它不起作用,有什么想法吗?@Arne - Arpit Suthar
重定向时发送@RequestBody怎么样? - undefined

27

看到这个问题,很惊讶没有人提到 RedirectView。我刚刚测试过,您可以使用以下方式以清洁的100% Spring 方式解决此问题:

@RestController
public class FooController {

    @RequestMapping("/foo")
    public RedirectView handleFoo() {
        return new RedirectView("some-url");
    }
}

有没有想法如何设置带有上下文路径的URL? - Bahij.Mik
@Bahij.Mik 我猜你在问servlet上下文路径 - 以下内容告诉我,你可以自动装配servlet上下文并从中检索路径 链接 - DhatGuy

3

redirect 意味着 http 状态码 302,在 springMVC 中表示 Found

这里有一个实用方法,可以放在某种类型的 BaseController 中:

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

但有时候可能希望返回 301 状态码,表示 永久移动

这种情况下,可以使用以下实用方法:

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

0

由于重定向通常需要经过不直接的路径,我认为抛出异常并稍后处理是我最喜欢的解决方案。

使用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
    );
  }
}

在我的情况下,异常类:

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

我是这样触发它的:

if (null == remoteUser)
  throw new NotLoggedInException("please log in", LOGIN_URL);

-12
如果您的 @RestController 返回一个字符串,您可以使用类似以下代码的方式:
return "redirect:/other/controller/";

这种重定向只适用于GET请求,如果您想使用其他类型的请求,请使用HttpServletResponse。


18
这将只适用于 @Controller,但不适用于特殊版本的 @Controller,即添加了 @ResponseBody 注释的 @RestController。Spring框架会自动将返回值转换并写入HTTP响应。例如下面的代码:@RestController public class RedirectController { @RequestMapping("/redirect") public String redirect() { return "redirect:/other/controller/"; } }如果我们尝试访问该URL curl localhost:8080/redirect,我们将只看到 redirect:/other/controller/ 作为结果。 - Anton Balaniuc
4
安东·巴拉纽克是正确的,阅读您整个答案仍然不正确。在RestController中,它将只返回字符串"redirect:/other/controller",它将不会重定向。 - S. Jacob Powell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接