Ответ 1
Поскольку spring 3.1, RequestMappingHandlerAdapter
предоставляет флаг с именем ignoreDefaultModelOnRedirect
, который можно использовать для предотвращения использования содержимого модели defautl, если контроллер перенаправляет.
Используя Spring MVC 3.0.0.RELEASE, у меня есть следующий контроллер:
@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, проверяет объект команды, сохраняет информацию в базе данных и возвращает номер дела.
Все отлично работает, за исключением случаев, когда я перенаправляю на страницу incomingDetails.htm, я получаю URL-адрес, который выглядит следующим образом:
http://localhost:8080/project/intakeDetails.htm?caseNumber=1&users=user1&users=user2&users=user3&users=user4...
Как предотвратить сбор пользовательской коллекции в URL?
Поскольку spring 3.1, RequestMappingHandlerAdapter
предоставляет флаг с именем ignoreDefaultModelOnRedirect
, который можно использовать для предотвращения использования содержимого модели defautl, если контроллер перенаправляет.
model.asMap().clear();
return "redirect:" + news.getUrl();
:)
Нет никаких хороших способов решения этой проблемы (т.е. без создания пользовательских компонентов без чрезмерного количества явной конфигурации xml и без ручного создания экземпляра RedirectView
).
Вы можете либо создать экземпляр RedirectView
вручную, используя его конструктор с 4 аргументами, либо объявить следующий bean в своем контексте (рядом с другими разрешениями представления):
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;
}
}
Аннотация метода @ModelAttribute
предназначена для использования отображения ссылочных данных на уровне представления. Я не могу сказать точно в вашем случае, но я бы не сказал, что набор пользователей, квалифицированных как справочные данные. Я предлагаю вам передать эту информацию через модель явно в ваши методы @RequestMapping
-annotated handler.
Если вы все еще хотите использовать @ModelAttribute
, здесь есть запись , в которой обсуждается проблема с перенаправлением.
Но все предыдущие примеры имеют общая проблема, так как все @ModelAttribute методы выполняются до того, как обработчик выполняется, если обработчик возвращает перенаправить данные модели будут добавлены к URL-адресу в качестве строки запроса. Эта следует избегать любой ценой, поскольку это может раскрывать некоторые секреты о том, как вы собрали ваше приложение.
Его предлагаемое решение (см. часть 4 блога) заключается в использовании HandlerInterceptorAdapter
, чтобы сделать общие ссылочные данные видимыми для представления, Поскольку справочные данные не должны быть тесно связаны с контроллерами, это не должно создавать проблем, конструктивно.
Я знаю, что этот вопрос и ответ устарел, но я наткнулся на него после того, как у меня возникли аналогичные проблемы, и там не так много другой информации, которую я мог найти.
Я думаю, что принятый ответ не очень хороший. Ответ прямо под ним axtavt намного лучше. Вопрос заключается не в том, имеет ли смысл аннотировать атрибуты модели на контроллере. Это о том, как выдать "чистую" переадресацию из контроллера, который обычно использует ModelAttributes. Сам контроллер обычно требует справочных данных, но иногда ему необходимо перенаправить где-то еще в исключительные условия или что-то еще, и передача ссылочных данных не имеет смысла. Я думаю, что это правильный и общий шаблон.
(Fwiw, я неожиданно столкнулся с этой проблемой с Tomcat. Перенаправления просто не работали, и я получал нечетные сообщения об ошибках вроде: java.lang.ArrayIndexOutOfBoundsException: 8192. В конечном итоге я определил, что максимальная длина заголовка Tomcat равна 8192. Я не понимал, что атрибуты ModelAttributes автоматически добавляются к URL-адресу переадресации, и это приводит к тому, что длина заголовка превышает длину заголовка Tomcat max.)
Я реализовал вариант ответа Sid с меньшим количеством копирования и вставки:
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:
<bean id="viewResolver" class="com.example.RedirectsNotExposingModelUrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>
В моем приложении у меня нет прецедентов для отображения атрибутов модели при перенаправлении, поэтому я расширил org.springframework.web.servlet.view.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 это должно быть поведение по умолчанию при перенаправлении
Или сделайте запрос POST одним. Полученные запросы будут отображать только атрибуты модели в качестве параметров запроса, отображаемых в 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;
}
Здесь вы сделали его возвратом Collection of String. Сделайте его сборкой пользователя (это может быть строка обертывания класса, представляющая пользователя, или класс с кучей данных, относящихся к пользователю). Проблема возникает только со строками. Если возвращенная коллекция содержит любой другой объект, это никогда не произойдет. Однако это всего лишь обходной путь, и может быть, не требуется вообще. Только мои два цента. Просто сделайте это как -
@ModelAttribute("users")
public Collection<User> getUsers(){
return this.users;
}
попробуйте добавить код ниже в servlet-config.xml
<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" />
иногда это решит проблему.