简介
在spring mvc中有些交互的场景中,我们需要将一些提交的内容在多个请求中可以进行传递。这种传递不仅仅是一些简单的参数,有时候希望是一些对象的传递。于是这就牵涉到一个多个请求中参数传递的问题。我们都知道,http请求它本身是无状态的,在一个请求场景中构造的参数到下一个请求中就已经丢失了。那么该怎么来实现参数传递呢?总的来说,有3种办法,一种是通过session的方式,因为相当于将需要传递数据缓存到服务器上,所以对于多个后续的请求都可以访问。一种是通过url template的方式,还有一种就是通过flash attribute。在本文中我们重点讨论一下url template和flash attribute这两种方式。
url template
url template的方式其实比较简单,它主要就是通过在提交表单之后指定后续跳转路径的参数来达到。比如如下的项目。
首先我们定义领域对象模型:
package com.springinpractice.ch03.model;
public class Member {
private String firstName;
private String lastName;
public Member() {}
public Member(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String toString() {
return firstName + " " + lastName;
}
}
然后定义一个提交表单的页面:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Nominee a member for the award</title> </head> <body> <h1>Nominee a member for the award</h1> <form:form modelAttribute="nominee"> <div>First name: <form:input path="firstName"/></div> <div>Last name: <form:input path="lastName"/></div> <div><input type="submit" value="Submit"></div> </form:form> </body> </html>
这两个部分的内容比较简单,重点是处理请求的controller部分:
@Controller
public class NomineeController {
private static final Logger logger =
LogManager.getLogger(NomineeController.class);
@RequestMapping(value = "/form", method = RequestMethod.GET)
public String form(@ModelAttribute("nominee") Member member) {
return "form";
}
@RequestMapping(value = "/form", method = RequestMethod.POST)
public String processFormData(@ModelAttribute("nomiee") Member member,
Model model) {
logger.info("Processing nominee: " + member);
Map<String, Object> map = model.asMap();
logger.info("model[member]=" + map.get("member"));
logger.info("model[nominee]=" + map.get("nominee"));
model.addAttribute("firstName", member.getFirstName());
return "redirect:/members/{firstName}";
}
@RequestMapping(value = "/members/{firstName}")
public String displayMember(@PathVariable String firstName,
Model model) {
logger.info("firstName: " + firstName);
model.addAttribute("firstName", firstName);
return "member";
}
}
在上述代码中,form方法用来展示提交表单的页面。重点在processForm方法中。我们定义了Model类型的参数并在其中定义了一个attribute "firstName"。完成设置之后然后跳转到页面/members/{firstName}。和普通的直接返回一个字符串不一样,这里因为定义了可以通过model的方式传递参数到jsp页面,它通过一种参数绑定解析的方式来解析参数firstName。这种方式还要一个好处,就是避免里面夹杂进一些非法的参数。
在后面的跳转处理方法中,也就是displayMember方法中,我们可以获取传递的参数,记录到日志中并在后面的页面中显示。
这个时候如果启动服务器,进入页面http://localhost:8080/FirstSpringWeb/form,我们将看到如下的内容:
在添加好必要的表单数据之后点击提交,页面将显示如下的内容:
这样,这种传递的方式效果就达到了。总结起来,它无非就是通过在前面提交表单的时候设定好后面跳转的页面是参数化的。比如我前面的跳转页面路径里设置{firstName}为frank,则后面跳转的页面则为http://localhost:8080/FirstSpringWeb/members/frank 。这相当于一种很简单的页面参数传递。
flash attribute
上面的那种方式只是简单的传递了一个url的参数,在一些情况下还是可以的。但是如果我们需要传递整个对象呢?前面的这种方式就不可行了。我们需要用到另外一个东西。那就是RedirectAttributes,这个对象可以用于请求之间参数的传递。我们对前面的示例做一个如下的修改:
@RequestMapping(value = "/form", method = RequestMethod.POST)
public String processFormData(@ModelAttribute("nomiee") Member member,
RedirectAttributes model) {
logger.info("Processing nominee: " + member);
Map<String, Object> map = model.asMap();
logger.info("model[member]=" + map.get("member"));
logger.info("model[nominee]=" + map.get("nominee"));
model.addFlashAttribute("member", member);
return "redirect:/thanks";
}
@RequestMapping(value = "/thanks", method = RequestMethod.GET)
public String thanks() {
return "thanks";
}
和前面的表单处理方式不一样,这里方法签名里的参数由Model改为RedirectAttributes,而且由原来model方法里的addAttribute改为addFlashAttribute。后面跳转之后显示页面的方法则看起来很简单,就是一个直接显示页面的代码。
我们再来看显示的jsp页面代码:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Thanks</title> </head> <body> <h1>Thanks</h1> <p>Thanks for nominating ${member}.</p> </body> </html>
这里比较有意思的地方是我们可以显示了一个参数${member}。如果我们尝试一下在页面中去显示一个并不存在的参数,页面会显示空白的内容,在这里会怎么样呢?
我们启动页面,输入如下参数:
提交后我们将看到如下的内容:
为什么上面的内容可以被传递呢?这个RedirectAttributes又不是会施魔法,肯定有什么办法来传递。其实这里传递的方式本质上还是用到了session,不过这种方式只是临时借用了一下session。它在每次发生第一个请求的时候,也就是当我们刚提交表单的时候,会将我们设定的参数放到session中。在下一个请求,也就是我们跳转到后面的那个显示内容的请求时,它会从session里来取我们保存的内容。像这里就是因为保存了member这个对象,而且它对应的参数名是"member",所以在后面的显示页面里就有内容。但是在这个请求结束后,它们又会将我们在session里保存的内容给去掉。
所以说,上述的办法只是对一种使用session临时存放数据的方法的封装,让我们用起来简单一点而已。对于上述两个示例的详细实现可以参照后面附件里的内容。
总结
在一些请求之间传递参数的方法有若干种,主要是通过session, url template, redirect attribute等。其实它们本质上无非就是要将一个请求里的消息传递给下一个请求。所以要么就是用简单的将参数放到请求路径里,要么就是将数据放到某个存储的地方,让后面的请求去读取。也没什么特别的。