欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringMVC传递参数到重定向后的页面

程序员文章站 2022-04-17 17:03:17
...

RedirectAttributes传递参数到重定向后的页面

Servlet容器在页面跳转时有两种方式,forward和redirect的,其中forward时应用的是在服务端的跳转,应用的是同一个request。而redirect是服务端通过响应301和对应的新地址告诉浏览器让浏览器重新请求新的地址。第一次请求和第二次请求使用的不是同一个request的。所以这种情况下,不能直接通过request传递参数到新的页面。SpringMVC默认使用的是forward请求,如果需要使用redirect跳转新页面,可以使用redirect:前缀。forward请求时添加到Model中的参数都可以被传递到新的页面,而通过redirect:跳转到新页面时,默认会自动把当前Model中包含的原始类型的属性及原始类型的Collection/Array作为查询参数传递下去。比如对于下面的处理器映射,如果我们请求/redirect/abc,则会自动跳转到/abc,并且会附上查询参数?a=1&b=2&c=1&c=2&c=3,所以浏览器会自动请求/abc?a=1&b=2&c=1&c=2&c=3

@RequestMapping("/redirect/{target}")
public String redirectTo(@PathVariable("target") String redirectTo, Map<String, Object> model) {
    model.put("a", "1");
    model.put("b", 2);
    model.put("c", Arrays.asList(1, 2, 3));
    return "redirect:/" + redirectTo;
}

对于复杂的对象,通过查询参数的方式就不能自动传递了。这个时候我们就可以使用RedirectAttributes了,它是一种特殊的Model。它的定义如下:

public interface RedirectAttributes extends Model {

	@Override
	RedirectAttributes addAttribute(String attributeName, Object attributeValue);

	@Override
	RedirectAttributes addAttribute(Object attributeValue);

	@Override
	RedirectAttributes addAllAttributes(Collection<?> attributeValues);

	@Override
	RedirectAttributes mergeAttributes(Map<String, ?> attributes);

	/**
	 * Add the given flash attribute.
	 * @param attributeName the attribute name; never {@code null}
	 * @param attributeValue the attribute value; may be {@code null}
	 */
	RedirectAttributes addFlashAttribute(String attributeName, Object attributeValue);

	/**
	 * Add the given flash storage using a
	 * {@link org.springframework.core.Conventions#getVariableName generated name}.
	 * @param attributeValue the flash attribute value; never {@code null}
	 */
	RedirectAttributes addFlashAttribute(Object attributeValue);

	/**
	 * Return the attributes candidate for flash storage or an empty Map.
	 */
	Map<String, ?> getFlashAttributes();
}

通过继承自Model的addAttribute等方法添加的属性,在重定向时会丢失或者以查询参数的方式传递。通过addFlashAttribute添加的属性则可以自动传递到新的页面的Model中,其内部会把它放到一个FlashMap中,区分input和output。由FlashMapManager管理,默认实现是基于Session的实现,即SessionFlashMapManager。redirect需要传参数到新页面时我们可以像如下这样,为处理器方法声明一个RedirectAttributes类型的参数,然后往其中通过addFlashAttribute添加需要传递的参数。在如下示例中我们通过addAttribute传递的参数key1和key2将通过附加为查询参数的方式传递,而通过addFlashAttribute传递的modelAttr1、modelAttr2和listAttr则会通过FlashMap传递。

@RequestMapping("/src")
public String index(RedirectAttributes redirectAttributes) {
    
    /**
     * addAttribute中的内容会作为redirect的URL的查询参数传递,即会以
     * /redirectattributes/target?key1=value1&key2=value2的形式传递,
     * 其中的value是以String的形式传递的,添加进去时会把它转换为String,如果内部没有对应的转换器支持则将
     * 抛出异常。具体可以参考RedirectAttributesModelMap中的对应实现
     */
    redirectAttributes.addAttribute("key1", "value1")
        .addAttribute("key2", "value2");
    
    /**
     * addFlashAttribute中的内容会存放到Session中,且在一次页面跳转后失效。
     */
    redirectAttributes.addFlashAttribute("modelAttr1", "modelAttr1Value1")
        .addFlashAttribute("modelAttr2", "modelAttr1Value2")
        .addFlashAttribute("listAttr", Arrays.asList(1, 2, 3));
    
    return "redirect:/redirectattributes/target";
}

除了通过RedirectAttributes传递参数外,我们也可以直接获取角色为output的FlashMap,通过往其中添加属性来传递。其实使用RedirectAttributes时底层也是通过角色为Output的FlashMap来传递的。示例如下。

@RequestMapping("/src")
public String index(HttpServletRequest request) {
    
    FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
    flashMap.put("abc", 111);
    return "redirect:/redirectattributes/target";
}

在重定向之后通过FlashMap传递的参数会自动到Model中,所以我们可以从Model中获取对应的参数,如果是在页面上,我们可以直接从request中获取。如果是在Controller方法中,则可以通过在方法参数上使用@ModelAttribute获取,也可以直接定义Model或Map类型的方法参数,然后从Model或Map中获取。在如下示例中的方法参数modelAttr1就是从Model中获取参数modelAttr1,而方法体里面也定义了从model参数中获取参数modelAttr1。

@RequestMapping("/target")
public String target(@ModelAttribute("modelAttr1") String modelAttr1, @RequestParam("key1") String key1, Map<String, Object> model) {
    //获取通过FlashMap传递过来的modelAttr1的值
    Object attr1 = model.get("modelAttr1");
    return "redirect_attributes_target";
}

我们也可以直接从Input角色的FlashMap中获取这些信息,其实底层也是从FlashMap中获取到的redirect传递过来的参数,然后把它们丢到Model中。示例如下:

@RequestMapping("/target")
public String target(HttpServletRequest request) {
    Map<String, ?> map = RequestContextUtils.getInputFlashMap(request);
    Object attr1 = map.get("modelAttr1");
    return "redirect_attributes_target";
}

需要说明的是redirect传递的参数在Session中只存在一会,会在请求到达重定向后的页面后从session中清除。

(本文基于Spring4.1.0所写)