Как предотвратить появление переменных 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 56

13 ответов:

С весны 3.1 the RequestMappingHandlerAdapter предоставляет флаг под названием ignoreDefaultModelOnRedirectкоторый можно использовать для предотвращения использования содержимого модели defautl, если контроллер перенаправляет.

model.asMap().clear();
return "redirect:" + news.getUrl();

:)

нет хороших способов решить эту проблему (т. е. без создания пользовательских компонентов, без чрезмерного количества явной конфигурации 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;
      }

попробуйте добавить следующий код в тег servlet-config.xml

<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" />

иногда это решит проблему.