Spring MVC@RestController 和重定向

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

Spring MVC@RestController 可以吗? 如果可以的话,你能举个例子吗?

125815 次浏览

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


}

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

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.

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

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

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