Spring MVC请求转发疑问:清空AdDto标题后转发仍带旧值?
问题场景
我在Spring MVC开发中实现了如下修改广告的接口:
@RequestMapping("/modify") public String modifyAd(AdDto adDto, Model model){ if (adService.modifyAd(adDto)){ adDto.setTitle(""); model.addAttribute(PageCodeEnum.KEY,PageCodeEnum.MODIFY_SUCCESS); }else { model.addAttribute(PageCodeEnum.KEY,PageCodeEnum.MODEFY_FAILED); } return "forward:search"; }
从JSP获取的adDto.title值为'芒果冰淇淋11',我已经将其设置为空字符串,随后转发至标注@RequestMapping(value = "/search",method = RequestMethod.POST)的queryByTitle方法,但该方法中仍能获取到旧的标题值,这是为什么?
问题原因
嘿,这个问题我之前踩过坑,其实本质是Spring MVC转发时的请求参数绑定逻辑在搞鬼:
你在modifyAd方法里修改的adDto.setTitle(""),只是修改了当前方法内存中的AdDto对象实例,但当你用forward:search做服务器内部转发时,Spring MVC会重新执行一次请求参数绑定流程——它会从原始的HttpServletRequest请求对象里读取参数,重新创建并填充一个新的AdDto对象,然后传给queryByTitle方法。而原始请求里的title参数还是最初从JSP传过来的'芒果冰淇淋11',所以新的AdDto自然还是旧值。
简单说:你改的是内存里的临时对象,转发时用的是原始请求里的参数,两者不是一回事。
解决方案
给你几个可行的解决思路,按需选择:
方案一:改用重定向(推荐,符合PRG模式)
转发是服务器内部复用原始请求,而重定向是让客户端发起全新的请求,原始请求参数不会被携带。这种方式还能避免表单重复提交的问题:
@RequestMapping("/modify") public String modifyAd(AdDto adDto, RedirectAttributes redirectAttrs){ if (adService.modifyAd(adDto)){ // 用FlashAttribute传递提示信息,重定向后不会丢失 redirectAttrs.addFlashAttribute(PageCodeEnum.KEY,PageCodeEnum.MODIFY_SUCCESS); }else { redirectAttrs.addFlashAttribute(PageCodeEnum.KEY,PageCodeEnum.MODIFY_FAILED); } // 重定向到search接口,新请求无旧title参数 return "redirect:search"; }
方案二:清空请求中的title参数
如果你一定要用转发,可以通过包装HttpServletRequest来修改请求参数:
@RequestMapping("/modify") public String modifyAd(AdDto adDto, Model model, HttpServletRequest request){ if (adService.modifyAd(adDto)){ // 用包装类覆盖title参数的取值 HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request) { @Override public String getParameter(String name) { if ("title".equals(name)) { return ""; } return super.getParameter(name); } @Override public String[] getParameterValues(String name) { if ("title".equals(name)) { return new String[]{""}; } return super.getParameterValues(name); } }; model.addAttribute(PageCodeEnum.KEY,PageCodeEnum.MODIFY_SUCCESS); // 转发时使用包装后的请求对象 ModelAndView mv = new ModelAndView("forward:search"); mv.addAllObjects(model.asMap()); return mv.getViewName(); }else { model.addAttribute(PageCodeEnum.KEY,PageCodeEnum.MODIFY_FAILED); } return "forward:search"; }
方案三:让search方法优先从Model获取AdDto
在modifyAd方法里把修改后的AdDto放到Model中,然后在queryByTitle方法里用@ModelAttribute注解指定从Model中获取对象,跳过请求参数绑定:
// 修改modify方法,将修改后的adDto存入Model @RequestMapping("/modify") public String modifyAd(AdDto adDto, Model model){ if (adService.modifyAd(adDto)){ adDto.setTitle(""); model.addAttribute("adDto", adDto); // 存入Model model.addAttribute(PageCodeEnum.KEY,PageCodeEnum.MODIFY_SUCCESS); }else { model.addAttribute(PageCodeEnum.KEY,PageCodeEnum.MODIFY_FAILED); } return "forward:search"; } // search方法通过@ModelAttribute获取Model中的adDto @RequestMapping(value = "/search",method = RequestMethod.POST) public String queryByTitle(@ModelAttribute("adDto") AdDto adDto, Model model){ // 这里的adDto就是modify方法里修改后的对象,title为空 // 后续业务逻辑... }
内容的提问来源于stack exchange,提问作者武新楠

