如何防止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" />
有时这将解决问题。