SpringMVC中的HTTP跳轉
1,forward重定向是在容器內部實現的同一個Web應用程序的重定向,所以forward方法只能重定向到同一個Web應用程序中的一個資源,redirect方法可以重定向到任何URL。
3,forward可以將請求中包含的數據傳遞到跳轉後的地址。redirect則不能,如果想傳遞參數,只能放在請求行中。
二,301與302跳轉
public void handleReturnValue(
Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws Exception {
if (returnValue == null) {
return;
}
else if (returnValue instanceof String) {
String viewName = (String) returnValue;
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
上面方法中的isRedirectViewName就是判斷返回的字符串是否以"redirect:"開頭,如果是則設置一個跳轉的標誌到ModelAndViewContainer 中。若Controller方法返回的直接是RedirectView,則對應的結果處理器就是ViewMethodReturnValueHandler,它和ViewNameMethodReturnValueHandler的處理過程差不多,代碼如下: public void handleReturnValue(
Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws Exception {
if (returnValue == null) {
return;
}
else if (returnValue instanceof View){
View view = (View) returnValue;
mavContainer.setView(view);
if (view instanceof SmartView) {
if (((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
}
else {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
throws IOException {
String targetUrl = createTargetUrl(model, request);
targetUrl = updateTargetUrl(targetUrl, model, request, response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
if (!CollectionUtils.isEmpty(flashMap)) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build();
flashMap.setTargetRequestPath(uriComponents.getPath());
flashMap.addTargetRequestParams(uriComponents.getQueryParams());
}
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
flashMapManager.saveOutputFlashMap(flashMap, request, response);
sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);
}
protected void sendRedirect(
HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible)
throws IOException {
String encodedRedirectURL = response.encodeRedirectURL(targetUrl);
if (http10Compatible) {
if (this.statusCode != null) {
response.setStatus(this.statusCode.value());
response.setHeader("Location", encodedRedirectURL);
}
else {
// Send status code 302 by default.
response.sendRedirect(encodedRedirectURL);
}
}
else {
HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
response.setStatus(statusCode.value());
response.setHeader("Location", encodedRedirectURL);
}
}
HTTP跳轉響應非常簡單,只需要設置跳轉的狀態碼和需要跳轉的地址。由上面代碼可知,默認情況下spring發送的是302跳轉,如果想發送301跳轉可以在返回的RedirectView中設置HTTP的狀態碼。通過ResponseStatus註解指定返回的狀態碼是沒有用的。四,spring對forward調整的處理
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());
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);
}
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine which request handle to expose to the RequestDispatcher.
HttpServletRequest requestToExpose = getRequestToExpose(request);
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, requestToExpose);
// Expose helpers as request attributes, if any.
exposeHelpers(requestToExpose);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(requestToExpose, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(requestToExpose, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(requestToExpose, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
exposeForwardRequestAttributes(requestToExpose);
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.forward(requestToExpose, response);
}
}
上面的方法先獲取一個RequestDispatcher,然後通過它將請求轉發到了新的路徑上。這些都是利用Servlet的標準,具體內部的實現可以在tomcat的源碼中查找。