如何防止Spring 3.0 MVC @ModelAttribute变量出现在URL中?

时间:2010-01-29 16:53:35

标签: java spring spring-mvc

使用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;

  }

}

Controller从填写HTML表单的命令对象中读取Intake信息,验证命令对象,将信息保存到数据库,并返回案例编号。

一切都很好,除了当我重定向到intakeDetails.htm页面时,我得到一个如下所示的URL:

http://localhost:8080/project/intakeDetails.htm?caseNumber=1&users=user1&users=user2&users=user3&users=user4...

如何阻止用户集合显示在网址中?

13 个答案:

答案 0 :(得分:38)

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

:)

答案 1 :(得分:30)

从3.1版开始,RequestMappingHandlerAdapter提供了一个名为ignoreDefaultModelOnRedirect的标志,如果控制器重定向,您可以使用该标志来防止使用defautl模型的内容。

答案 2 :(得分:18)

没有好的方法可以解决这个问题(即没有创建自定义组件,没有过多的显式xml配置,也没有RedirectView的手动实例化)。

您可以通过其4参数构造函数手动实例化RedirectView,或在您的上下文中声明以下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;
    }
}

答案 3 :(得分:18)

@ModelAttribute方法注释旨在用于exposing reference data到视图层。在你的情况下,我不能肯定地说,但我不会说一组用户符合参考数据。我建议您在@RequestMapping - 带注释的处理程序方法中明确地将此信息传递给模型。

如果您仍想使用@ModelAttribute,则此处有一个blog条目,可以讨论重定向问题。

  

但是之前的所有例子都有   常见问题,就像所有@ModelAttribute一样   方法在处理程序之前运行   如果处理程序返回一个,则执行   将添加重定向模型数据   将url作为查询字符串。这个   应该不惜一切代价避免它   可能会揭露你的一些秘密   已经整理了你的申请。

他建议的解决方案(参见博客的第4部分)是使用HandlerInterceptorAdapter使视图可以看到公共参考数据。由于参考数据不应与控制器紧密耦合,因此在设计方面不应造成问题。

答案 4 :(得分:5)

我知道这个问题和答案已经很久了,但是在我自己遇到类似的问题之后我偶然发现它并且我找不到其他很多信息。

我认为接受的答案不是很好。 axtavt下方的答案要好得多。问题不在于控制器上的注释模型属性是否有意义。它是关于如何从通常使用ModelAttributes的控制器中发出“干净”重定向。控制器本身通常需要参考数据,但有时它需要在其他地方重定向以获得异常条件或其他任何东西,并且传递参考数据没有意义。我认为这是一种有效且常见的模式。

(Fwiw,我用Tomcat意外地遇到了这个问题。重定向根本不起作用,我得到了奇怪的错误消息,如:java.lang.ArrayIndexOutOfBoundsException:8192。我最终确定Tomcat的默认最大标头长度为8192。我没有意识到ModelAttributes被自动添加到重定向URL,这导致标头长度超过Tomcat的最大标头长度。)

答案 5 :(得分:5)

我实现了Sid's answer的变体,减少了复制和粘贴:

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>

答案 6 :(得分:2)

在我的应用程序中,我没有任何用于在重定向中公开模型属性的用例,因此我扩展了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>

答案 7 :(得分:2)

手动创建一个为我工作的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); 
}

恕我直言,这应该是重定向时的默认行为

答案 8 :(得分:1)

或者,将该请求设为POST。获取请求只会将模型属性显示为URL中显示的请求参数。

答案 9 :(得分:1)

以下是如何使用基于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

}

答案 10 :(得分:0)

请勿使用@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()中发生验证错误。如果您只想返回表单的逻辑名称,您还必须记住与用户重新填充模型,否则表单将无法正确设置。

答案 11 :(得分:0)

如果有助于您的事业,有一种解决方法。

      @ModelAttribute("users")
      public Collection<String> getUsers(){
           return this.users;
      }

这里你已经返回String的Collection。使其成为User的集合(它可以是表示用户的类包装字符串,或者是具有关于用户的一堆数据的类)。只有字符串才会出现问题。如果返回的Collection包含任何其他对象,则不会发生这种情况。但是,这只是一种解决方法,可能根本不需要。只是我的两分钱。就像 -

      @ModelAttribute("users")
      public Collection<User> getUsers(){
           return this.users;
      }

答案 12 :(得分:0)

尝试将以下代码添加到 servlet-config.xml

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

有时这会解决问题。