对 SpringMVC 有一定了解的同学对「redirect」和「forward」的语法都不陌生。
@GetMapping("/product/{id}")
public String forward(@PathVariable("id") String id) {
return "forward:/product2/{id}";
}
@GetMapping("/hello")
public String redirect() {
return "redirect:/world";
}
@GetMapping("/somepage")
public String toPage() {
return "templates/hello";
}
如上面的例子,请求/somepage
会访问我们指定的页面,也是最常见的用法之一。而请求/product/111
将 forward 到 /product2/111
,请求 /hello
将 redirect 到 /world
。这两种用法由 SpringMVC 提供,可以满足大部分的重定向需求。
但实际项目中,往往需要考虑 SEO 的因素。SpringMVC 提供的 redirect 机制实际上是 302 跳转,302 跳转代表的是一种临时的状态。实际开发过程中,我们往往需要通知搜索引擎页面 A 已经永久的替换为 页面 B 了,以便搜索引擎将旧页面权重传递到新页面。此时应该使用 301 跳转。
SpringMVC 没有提供 301 跳转,那么我们应该怎么实现呢?最直观的办法显然是
public void redirect301(HttpServletResponse response) {
response.setStatus(301);
response.setHeader("Location", "xxx");
}
相比原生提供的重定向机制,这种实现显然不够优雅。既然如此,我们何不借鉴下 SpringMVC 的实现呢?
全局搜索「redirect:」、「forward:」,很容易找到他的实现org.springframework.web.servlet.view.UrlBasedViewResolver.createView
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
view.setHosts(getRedirectHosts());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
}
处理逻辑一目了然,我们的解决方案也呼之欲出了:继承 UrlBasedViewResolver 并重写 createView 方法,处理我们自定义的前缀。
private static final String REDIRECT_301_URL_PREFIX = "redirect 301:";
@Override
protected View createView(String viewName, Locale locale) throws Exception {
if (!canHandle(viewName, locale)) {
return null;
}
if (viewName.startsWith(REDIRECT_301_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_301_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible(), false);
view.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
return (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
}
return super.createView(viewName, locale);
}
配置完成以后就可以优雅的进行 301 跳转了。
@GetMapping("/hello")
public String redirect301() {
return "redirect 301:/world";
}
完
网友评论