對 SpringMVC 有一定了解的同學(xué)對「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 提供,可以滿足大部分的重定向需求。
但實(shí)際項目中,往往需要考慮 SEO 的因素。SpringMVC 提供的 redirect 機(jī)制實(shí)際上是 302 跳轉(zhuǎn),302 跳轉(zhuǎn)代表的是一種臨時的狀態(tài)。實(shí)際開發(fā)過程中,我們往往需要通知搜索引擎頁面 A 已經(jīng)永久的替換為 頁面 B 了,以便搜索引擎將舊頁面權(quán)重傳遞到新頁面。此時應(yīng)該使用 301 跳轉(zhuǎn)。
SpringMVC 沒有提供 301 跳轉(zhuǎn),那么我們應(yīng)該怎么實(shí)現(xiàn)呢?最直觀的辦法顯然是
public void redirect301(HttpServletResponse response) {
response.setStatus(301);
response.setHeader("Location", "xxx");
}
相比原生提供的重定向機(jī)制,這種實(shí)現(xiàn)顯然不夠優(yōu)雅。既然如此,我們何不借鑒下 SpringMVC 的實(shí)現(xiàn)呢?
全局搜索「redirect:」、「forward:」,很容易找到他的實(shí)現(xiàn)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);
}
配置完成以后就可以優(yōu)雅的進(jìn)行 301 跳轉(zhuǎn)了。
@GetMapping("/hello")
public String redirect301() {
return "redirect 301:/world";
}
完