如何防止Spring 3.0 MVC @ModelAttributevariables出现在URL中?

使用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表单填充的命令对象的Intake信息,validation命令对象,将信息保存到数据库,并返回一个案例号。

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

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

如何防止用户集合显示在URL中?

自Spring 3.1以来, RequestMappingHandlerAdapter提供了一个名为ignoreDefaultModelOnRedirect的标志,如果控制器redirect,则可以使用该标志防止使用默认模型的内容。

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

🙂

没有好办法来解决这个问题(即不创build自定义组件,没有过多的显式xmlconfiguration,没有手动实例化RedirectView )。

你可以通过它的4参数构造函数手动实例化RedirectView ,或者在你的上下文(在其他视图parsing器附近)声明下面的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方法注释旨在用于将参考数据展示给视图层。 对于您的情况,我不能肯定地说,但我不会说一组用户作为参考数据。 我build议你在@RequestMapping -annotated处理方法中明确地将这些信息传递给模型。

如果你仍然想使用@ModelAttribute ,这里有一个博客条目,讨论redirect问题。

但是,所有前面的例子都有一个共同的问题,因为所有的@ModelAttribute方法都是在处理程序执行之前运行的,如果处理程序返回一个redirect,模型数据将作为查询string添加到url中。 这应该避免不惜一切代价,因为它可能会暴露你如何把你的应用程序放在一起的一些秘密。

他build议的解决scheme(参见博客的第4部分)是使用HandlerInterceptorAdapter来使公共参考数据对视图可见。 由于参考数据不应与控制器紧密耦合,因此这不应成为devise方面的问题。

我知道这个问题和答案是旧的,但是我自己也遇到了类似的问题,我也偶然发现了这个问题,并且没有很多其他信息可以find。

我认为接受的答案不是很好。 axtavt下面的答案好多了。 问题不在于在控制器上注释模型属性是否合理。 这是关于如何从通常使用ModelAttributes的控制器中发出“干净的”redirect。 控制器本身通常需要参考数据,但是有时需要redirect到其他地方以获得特殊的条件或者其他,并且传递参考数据是没有意义的。 我认为这是一个有效和普遍的模式。

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

我用较less的复制和粘贴实现了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; } } 

这也需要定义一个视图parsing器bean:

 <bean id="viewResolver" class="com.example.RedirectsNotExposingModelUrlBasedViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/> </bean> 

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

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

恕我直言,这应该是redirect时的默认行为

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

下面是如何使用基于Java的configuration(Spring 3.1 +我认为,用4.2testing):

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

有一个解决方法,如果它可以帮助你的事业。

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

在这里你已经使它返回String的集合。 使其成为一个用户集合(它可能是一个类包装string代表一个用户,或与一堆关于用户的数据类)。 问题只发生在string上。 如果返回的Collection包含任何其他对象,则不会发生这种情况。 但是,这只是一个解决方法,可能根本不需要。 只是我的两分钱。 只是让它像 –

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

尝试将下面的代码添加到servlet-config.xml中

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

有时这将解决问题。