001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.mvc.method.annotation; 018 019import org.springframework.core.MethodParameter; 020import org.springframework.util.PatternMatchUtils; 021import org.springframework.web.context.request.NativeWebRequest; 022import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 023import org.springframework.web.method.support.ModelAndViewContainer; 024import org.springframework.web.servlet.ModelAndView; 025import org.springframework.web.servlet.SmartView; 026import org.springframework.web.servlet.View; 027 028/** 029 * Handles return values of type {@link ModelAndView} copying view and model 030 * information to the {@link ModelAndViewContainer}. 031 * 032 * <p>If the return value is {@code null}, the 033 * {@link ModelAndViewContainer#setRequestHandled(boolean)} flag is set to 034 * {@code true} to indicate the request was handled directly. 035 * 036 * <p>A {@link ModelAndView} return type has a set purpose. Therefore this 037 * handler should be configured ahead of handlers that support any return 038 * value type annotated with {@code @ModelAttribute} or {@code @ResponseBody} 039 * to ensure they don't take over. 040 * 041 * @author Rossen Stoyanchev 042 * @since 3.1 043 */ 044public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler { 045 046 private String[] redirectPatterns; 047 048 049 /** 050 * Configure one more simple patterns (as described in {@link PatternMatchUtils#simpleMatch}) 051 * to use in order to recognize custom redirect prefixes in addition to "redirect:". 052 * <p>Note that simply configuring this property will not make a custom redirect prefix work. 053 * There must be a custom {@link View} that recognizes the prefix as well. 054 * @since 4.1 055 */ 056 public void setRedirectPatterns(String... redirectPatterns) { 057 this.redirectPatterns = redirectPatterns; 058 } 059 060 /** 061 * Return the configured redirect patterns, if any. 062 * @since 4.1 063 */ 064 public String[] getRedirectPatterns() { 065 return this.redirectPatterns; 066 } 067 068 069 @Override 070 public boolean supportsReturnType(MethodParameter returnType) { 071 return ModelAndView.class.isAssignableFrom(returnType.getParameterType()); 072 } 073 074 @Override 075 public void handleReturnValue(Object returnValue, MethodParameter returnType, 076 ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 077 078 if (returnValue == null) { 079 mavContainer.setRequestHandled(true); 080 return; 081 } 082 083 ModelAndView mav = (ModelAndView) returnValue; 084 if (mav.isReference()) { 085 String viewName = mav.getViewName(); 086 mavContainer.setViewName(viewName); 087 if (viewName != null && isRedirectViewName(viewName)) { 088 mavContainer.setRedirectModelScenario(true); 089 } 090 } 091 else { 092 View view = mav.getView(); 093 mavContainer.setView(view); 094 if (view instanceof SmartView) { 095 if (((SmartView) view).isRedirectView()) { 096 mavContainer.setRedirectModelScenario(true); 097 } 098 } 099 } 100 mavContainer.setStatus(mav.getStatus()); 101 mavContainer.addAllAttributes(mav.getModel()); 102 } 103 104 /** 105 * Whether the given view name is a redirect view reference. 106 * The default implementation checks the configured redirect patterns and 107 * also if the view name starts with the "redirect:" prefix. 108 * @param viewName the view name to check, never {@code null} 109 * @return "true" if the given view name is recognized as a redirect view 110 * reference; "false" otherwise. 111 */ 112 protected boolean isRedirectViewName(String viewName) { 113 if (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName)) { 114 return true; 115 } 116 return viewName.startsWith("redirect:"); 117 } 118 119}