Как предотвратить появление переменных Spring 3.0 MVC @ModelAttribute в URL-адресе?
С помощью Spring MVC 3.0.0.Отпустите, у меня есть следующий контроллер:
@Controller
@RequestMapping("/addIntake.htm")
public class AddIntakeController{
private final Collection<String> users;
public AddIntakeController(){
users = new ArrayList<String>();
users.add("user1");
users.add("user2");
// ...
users.add("userN");
}
@ModelAttribute("users")
public Collection<String> getUsers(){
return this.users;
}
@RequestMapping(method=RequestMethod.GET)
public String setupForm(ModelMap model){
// Set up command object
Intake intake = new Intake();
intake.setIntakeDate(new Date());
model.addAttribute("intake", intake);
return "addIntake";
}
@RequestMapping(method=RequestMethod.POST)
public String addIntake(@ModelAttribute("intake")Intake intake, BindingResult result){
// Validate Intake command object and persist to database
// ...
String caseNumber = assignIntakeACaseNumber();
return "redirect:intakeDetails.htm?caseNumber=" + caseNumber;
}
}
контроллер считывает информацию о приеме из объекта команды, заполненного из HTML-формы, проверяет объект команды, сохраняет информацию в базе данных и возвращает номер обращения.
все работает отлично, за исключением того, когда я перенаправляю на intakeDetails.htm страница, я получаю URL, который выглядит так:
http://localhost:8080/project/intakeDetails.htm?caseNumber=1&users=user1&users=user2&users=user3&users=user4...
Как я могу предотвратить пользователя Коллекция от отображения в URL-адресе?
13 ответов:
С весны 3.1 the
RequestMappingHandlerAdapter
предоставляет флаг под названиемignoreDefaultModelOnRedirect
который можно использовать для предотвращения использования содержимого модели defautl, если контроллер перенаправляет.
нет хороших способов решить эту проблему (т. е. без создания пользовательских компонентов, без чрезмерного количества явной конфигурации xml и без ручного создания экземпляра
RedirectView
).вы можете либо создать экземпляр
RedirectView
вручную с помощью конструктора с 4 аргументами или объявите следующий компонент в своем контексте (рядом с другими разрешителями представлений):public class RedirectViewResolver implements ViewResolver, Ordered { // Have a highest priority by default private int order = Integer.MIN_VALUE; // Uses this prefix to avoid interference with the default behaviour public static final String REDIRECT_URL_PREFIX = "redirectWithoutModel:"; public View resolveViewName(String viewName, Locale arg1) throws Exception { if (viewName.startsWith(REDIRECT_URL_PREFIX)) { String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length()); return new RedirectView(redirectUrl, true, true, false); } return null; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } }
The
@ModelAttribute
аннотация метода предназначена для выставление справочных данных к слою представления. Я не могу сказать наверняка в вашем случае, но я бы не сказал, что коллекция пользователей квалифицируется как справочные данные. Я предлагаю вам передать эту информацию через модель явно в вашем@RequestMapping
-аннотированные методы-обработчики.если вы все еще хотите использовать
@ModelAttribute
, есть блог запись здесь, которая обсуждает перенаправление проблема.но все предыдущие примеры есть общая проблема, как и все @ ModelAttribute методы выполняются перед обработчиком выполняется, если обработчик возвращает перенаправление данных модели будет добавлено к URL в строке запроса. Этот следует избегать любой ценой, как это может раскрыть некоторые секреты о том, как вы собрали ваше заявление.
его предлагаемое решение (см. Часть 4 блога) заключается в использовании
HandlerInterceptorAdapter
чтобы сделать общие справочные данные видимыми для представления. Поскольку справочные данные не должны быть тесно связаны с контроллерами, это не должно создавать проблем с точки зрения дизайна.
Я знаю, что этот вопрос и ответ старый, но я наткнулся на него после того, как у меня были подобные проблемы, и там не так много другой информации, которую я мог бы найти.
Я думаю, что принятый ответ не очень хороший. Ответ прямо под ним от axtavt намного лучше. Вопрос не в том, имеет ли смысл аннотирование атрибутов модели на контроллере. Речь идет о том, как выполнить "чистое" перенаправление из контроллера, который обычно использует ModelAttributes. Этот сам контроллер обычно требует справочных данных, но иногда ему нужно перенаправить куда-то еще для исключительных условий или чего-то еще, и передача справочных данных не имеет смысла. Я думаю, что это правильный и общий шаблон.
(Fwiw, я неожиданно столкнулся с этой проблемой с Tomcat. Перенаправления просто не работали, и я получал странные сообщения об ошибках, такие как: java.ленг.ArrayIndexOutOfBoundsException: 8192. В конце концов я определил, что заголовок Tomcat по умолчанию max длина составляет 8192. Я не понимал, что атрибуты ModelAttributes автоматически добавляются в URL-адрес перенаправления, и это приводило к тому, что длина заголовка превышала максимальную длину заголовка Tomcat.)
я реализовал вариант ОМР С меньшим количеством копирования и вставки участвуют:
public class RedirectsNotExposingModelUrlBasedViewResolver extends UrlBasedViewResolver { @Override protected View createView(String viewName, Locale locale) throws Exception { View view = super.createView(viewName, locale); if (view instanceof RedirectView) { ((RedirectView) view).setExposeModelAttributes(false); } return view; } }
для этого также требуется определить компонент разрешения представлений:
<bean id="viewResolver" class="com.example.RedirectsNotExposingModelUrlBasedViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/> </bean>
в моем приложении у меня нет прецедента для предоставления атрибутов модели в redirect, поэтому я расширил org.springframework.сеть.сервлет.вид.UrlBasedViewResolver для переопределения метода createView и используется объявленный в контексте приложения:
public class UrlBasedViewResolverWithouthIncludingModeAtttributesInRedirect extends UrlBasedViewResolver { @Override 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()); boolean exposeModelAttributes = false; return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible(), exposeModelAttributes); } // 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); } } <bean id="viewResolver" class="com.acme.spring.UrlBasedViewResolverWithouthIncludingModeAtttributesInRedirect"> </bean>
ручное создание объекта RedirectView работало для меня:
@RequestMapping(method=RequestMethod.POST) public ModelAndView addIntake(@ModelAttribute("intake")Intake intake, BindingResult result){ // Validate Intake command object and persist to database // ... String caseNumber = assignIntakeACaseNumber(); RedirectView rv = new RedirectView("redirect:intakeDetails.htm?caseNumber=" + caseNumber); rv.setExposeModelAttributes(false); return new ModelAndView(rv); }
IMHO это должно быть поведение по умолчанию при перенаправлении
или сделайте этот запрос почтовым. Запросы Get будут отображать только атрибуты модели в качестве параметров запроса, отображаемых в URL-адресе.
вот как это сделать с Java-конфигурацией (Spring 3.1+ я думаю, протестировано с 4.2):
@Configuration public class MvcConfig extends WebMvcConfigurationSupport { @Override @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(); adapter.setIgnoreDefaultModelOnRedirect(true); return adapter; } // possible other overrides as well }
не используйте
@ModelAttribute
. Храните пользователей вModelMap
явно. Вы делаете столько же с объектом команды в любом случае.@RequestMapping(method=RequestMethod.GET) public String setupForm(ModelMap model){ // Set up command object Intake intake = new Intake(); intake.setIntakeDate(new Date()); model.addAttribute("intake", intake); model.addAttribute("users", users); return "addIntake"; }
недостатком этого является то, что ошибка проверки имеет место в
addIntake()
. Если вы хотите просто вернуть логическое имя формы, вы также должны помнить о повторном заполнении модели пользователями, иначе форма не будет настроена правильно.
есть обходной путь, если это помогает вашей причине.
@ModelAttribute("users") public Collection<String> getUsers(){ return this.users; }
здесь вы сделали это вернуть коллекцию строк. Сделайте его коллекцией пользователя (это может быть строка обертывания класса, представляющая пользователя, или класс с кучей данных о пользователе). Проблема происходит только со строками. Если возвращенная коллекция содержит какой-либо другой объект, это никогда не происходит. Однако это всего лишь обходной путь, и может быть, не требуется вообще. Только мои два цента. Просто сделайте это, как -
@ModelAttribute("users") public Collection<User> getUsers(){ return this.users; }