SpringMVC就不用多介绍了,这篇文章总结一下URL重定向的数据保存问题,至于什么是URL重定向?在之前的博客中我有介绍过。
今天再写项目的时候发现:使用SpringMVC的Controller是可以对同一个URL返回不同的界面的。但是发现了一个问题,如果是一个登录的界面,用户名和密码正确之后返回给用户的是登后的主界面,但是!URL的地址没有变!
那意思就是说我的URL地址是在登录页面但是视图是登陆完成后的主界面了,这好像是有点不太对的吧......
其实正确的情况应该是:登录的界面的URL叫做/test/login,展现出了一个登录界面。主界面的URL叫做/test/Main,展现出的是主界面的视图。
现在就用用户登录这个例子练习一下重定向中的数据传输模式。
怎么去重定向?
但是问题来了:怎么样才可以把地址栏上的URL换掉呢?在这里可以使用重定向的方法来改变URL,首先,我总结了一张图,先放在这,然后再一步一步的细说。
现在对照着图来一步一步细说
一、客户端想要登录,访问到了登录的URL,这里就叫做/test/login吧。因为这是个SpringMVC项目,所以,前端控制器收到请求的URL之后,在已经注册的Controller中找到了登陆界面的Controller,叫做Index-Controller。
二、Index-Controller处理登录请求,它在数据库里核对了一下,发现用户名和密码是对的,所以它得把用户看到的界面和URL换成主界面的,所以它创建了ModelAndView对象(SpringMVC的底层对象),往对象里塞了点属性,并使用
-
//重定向的时候,不能写jsp文件的名字,必须写映射的URL地址
-
modelAndView.setViewName(
"redirect:/test/Main");
想把页面重定向到主界面去,让用户看到这些属性。
三、但是!重定向之后客户端用新请求去访问主界面,这样的话刚刚塞到ModelAndView那些属性不就凉了么。
在这里,我们可以使用上图的:RedirectAttribute类来保存之前获得的属性,这样就可以避免重定向之后数据丢失的问题。
就是说:原来我们是向ModelAndView中放属性的,但是现在我们只要往这个类里放就OK了,就可以让用户在主界面看到之前想保存的那一堆属性了。
四、现在看一下Index-Controller的代码吧
-
package root.controller;
-
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
import root.back.service.UserService;
-
import org.springframework.stereotype.Controller;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.bind.annotation.RequestParam;
-
-
import org.springframework.web.servlet.ModelAndView;
-
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
import javax.servlet.http.HttpSession;
-
import java.io.UnsupportedEncodingException;
-
import java.util.HashMap;
-
import java.util.Map;
-
-
-
//处理登录请求的后端控制器
-
-
//注意:@RequestParam注解中的required注解对表单提交中的属性是没有用的,就算不填它也会默认为空字符串,它只对GET请求中
-
//在url后加的key-value的属性有限制作用
-
@Controller
-
@RequestMapping(value = {
"/test"})
-
public
class LoginController {
-
-
private
static
final String CURRENT_USER =
"Now_user";
-
-
//业务层接口
-
@Autowired
-
private UserService userService;
-
-
-
//如果是GET方法请求的话,就直接给用户返回登录的页面,此页面表单请求的方法为POST
-
@RequestMapping(value = {
"/login"},method = {RequestMethod.GET})
-
public ModelAndView LoginGet(){
-
ModelAndView modelAndView =
new ModelAndView();
-
modelAndView.setViewName(
"index");
-
return modelAndView;
-
}
-
-
//该方法主要是用来处理表单提交上来的请求的,@RequestParam中的value属性就是表单中的属性名
-
//可以通过这种方法重新定义参数的名字
-
@RequestMapping(value = {
"login"},method = {RequestMethod.POST})
-
//让请求的url后面必须跟上一个叫做userName的属性,是用户的用户名
-
public ModelAndView LoginPost(@RequestParam(value = "userName") String userName,
-
//请求的url后必须跟上password属性,为用户当前的密码
-
@RequestParam(value = "password") String password,
-
//Spring MVC框架集成了Servlet请求响应等一系列参数,可以在有需要的时候使用
-
HttpServletRequest request, HttpServletResponse response,
-
HttpSession session, RedirectAttributes redirectAttributes) {
-
-
//这里是和后端交互的代码,如果是用户登录的话就在数据库中查找对应的用户信息
-
if(userName.isEmpty() || password.isEmpty()){
-
ModelAndView modelAndView =
new ModelAndView();
-
modelAndView.addObject(
"error",
"用户名或密码为空");
-
modelAndView.setViewName(
"index");
-
return modelAndView;
-
}
-
-
//用户名和密码不为空,进入登录的业务层
-
//如果登录业务失败,说明用户已经注册过了
-
if(!userService.login(userName, password)){
-
ModelAndView modelAndView =
new ModelAndView();
-
modelAndView.addObject(
"error",
"用户名或密码错误");
-
modelAndView.setViewName(
"index");
-
return modelAndView;
-
}
-
-
//到了这里就说明用户登录成功了
-
// System.out.println("用户名是:" + userName + "密码是:" + password);
-
//使用session进行会话跟踪
-
session.setAttribute(CURRENT_USER, userName);
-
-
//创建模型与视图类,返回给前端控制器
-
ModelAndView modelAndView =
new ModelAndView();
-
-
//重定向的时候,因为是客户端重新的请求,所以参数是不会被传到重定向页面的
-
-
//所以使用此方法,可以把属性放到一个叫做FlashMap的Map中
-
redirectAttributes.addFlashAttribute(
"userName",userName);
-
redirectAttributes.addFlashAttribute(
"password",password);
-
-
//使用重定向的时候不能写jsp的名字,要写url映射的路径
-
modelAndView.setViewName(
"redirect:/test/Main");
-
return modelAndView;
-
}
-
}
RedirectAttribute类是有两种方法来存属性的,我这里只用了一种,现在看一下上图中红色框中的内容。
总结一下:
1、addAttribute()方法直接把你的参数加到url的后面进行重定向,使用这个方法之后,虽然你成功的跳到了主界面,但是!页面的URL变成了:/test/Main?userName=10000&password=22222,一般的网站登录好像是没人这么干的吧,所以这就不做讲解了。而且要是想获取数据的话,可以直接用@RequestParam或者@ModelAttribute来获取。
2、但是如果使用addFlashAttribute()方法就没有那么多事了,这个方法的使用和上面的那个是一样的,唯一的区别就是传输数据的方法不一样。这个方法会把你加入的数据放在一个叫做FlashMap的Map中。在进行重定向的时候,这个Map中的数据会跟着新生成的Request一起到去请求重定向到的页面(也就是程序的的主界面),当重定向之后,这个Map里的数据会被清空,意思就是现在只有新页面的Request才有这些数据了。
当然,如果现在刷新一下页面,你会发现展示的数据(一开始你存储在FlashMap中的数据)没了。这其实也很好理解吧,刚刚说了,FlashMap在把数据给重定向生成的Request后,就把自己的数据删了。现在你刷新了一下页面相当于客户端又对这个URL产生了新请求,这个新请求是不可能有上一个请求中的信息的,所以避免了表单的重复提交。
FlashMap:由一个叫做FlashMapManager的管理器管理,一般默认使用的是SessionFlashMapManager,不过如果你想在你的Session中找到你的数据的话那是找不到的。
五、怎么才能拿到通过addFlashAttribute()存储在FlashMap中的属性呢?
刚刚是登录界面的Controller怎样才能把数据在重定向的过程中不丢失的问题,但是,数据是没有丢,但是还是得取的吧,只会存不会取那还是有点凉的。。这里取数据的操作是在主界面的Controller(就叫main-Controller吧)完成的。先看代码。
-
package root.controller;
-
-
import org.springframework.stereotype.Controller;
-
import org.springframework.web.bind.annotation.ModelAttribute;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.servlet.ModelAndView;
-
import org.springframework.web.servlet.support.RequestContextUtils;
-
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpSession;
-
import java.util.Map;
-
-
@Controller
-
@RequestMapping(value = {
"/test"})
-
public
class MainController {
-
-
@RequestMapping(value = {
"/Main"},method = {RequestMethod.GET})
-
public ModelAndView GetMain(
-
HttpServletRequest request, HttpSession session){
-
-
ModelAndView modelAndView =
new ModelAndView();
-
-
//对FlashMap中的参数进行提取,有两种方法
-
//第一种,使用RequestContextUtils(请求工具包),因为在重定向后FlashMap会把表单中的属性
-
//放在重定向新的请求中,所以可以获得请求中的FlashMap
-
Map<String,?> map = RequestContextUtils.getInputFlashMap(request);
-
//把FlashMap直接放入模型,传给前端控制器
-
modelAndView.addAllObjects(map);
-
//视图名传入
-
modelAndView.setViewName(
"main");
-
-
return modelAndView;
-
-
}
-
-
-
//第二种:使用@ModelAttribute注解
-
//因为FlashMap是处理这个url的初始化数据模型,所以可以通过这个注解拿到FlashMap的属性
-
@RequestMapping(value = {
"/Main"},method = {RequestMethod.POST})
-
public String PostMain(@ModelAttribute(value = "userName") String userName,
-
@ModelAttribute(value = "password") String password){
-
return
"main";
-
}
-
}
登录的Controller返回的视图名是:redirect:/test/Main,前端控制器找到了这个Controller,所以这个Controller负责渲染并返回视图。
渲染的话就是往视图里放数据,所以这里就要把FlashMap中的数据取出来了,先放张图。
虽然说,管理FlashMap的管理器默认是SessionFlashMapManager,保存在Session中。但是,通过Session是无法获得数据的。
方法一:使用RequestContextUtils.getInputFlashMap()来直接获取FlashMap(重点)
我们在Index-Controller中,把数据使用RedirectAttribute.addFlashAttribute()保存在FlashMap中,在重定向的时候,FlashMap中的数据被添加到了Request中,所以使用这个方法可以从Request中获取到FlashMap。
FlashMap本质上还是一个Map,所以这个方法的返回值是Map<String,?>,我们只要用一个Map来接收就可以了,然后我们就可以提取我们之前存储下的那些属性了。(就是可以直接在视图上使用 ${属性名} 来获取)。
方法二、通过(@ModelAttribute(value="属性名")String xxx)通过属性名称获取属性值(推荐)
刚刚已经说过了,一开始储存的数据在重定向的时候都放在Request里了,这些数据被作为初始化的模型(通俗的来讲就是这些数据被这个控制器的所有方法共享,即为共享数据),所以通过@ModelAttribute可以在Main-Controller中的任意一个方法里通过属性名获取这些属性。比上面的那个要简单多了,所以推荐使用这个方法。
六、页面展示
首先是登录/主界面的jsp。
Index.jsp:
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
-
<html>
-
<head>
-
<meta charset="UTF-8">
-
<title>主界面
</title>
-
</head>
-
<body>
-
-
-
-
<script>
-
function Logout() {
-
<%
-
session.invalidate();
-
%>
-
-
-
}
-
</script>
-
-
<p>登录成功
</p>
-
你的用户名为:
<p>${userName}
</p>
-
你的密码为:
<p>${password}
</p>
-
<a href="login" onclick="Logout">退出
</a>
-
-
-
</body>
-
</html>
main.jsp:
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
-
<html>
-
<head>
-
<meta charset="UTF-8">
-
<title>主界面
</title>
-
</head>
-
<body>
-
-
-
-
<script>
-
function Logout() {
-
<%
-
session.invalidate();
-
%>
-
-
-
}
-
</script>
-
-
<p>登录成功
</p>
-
你的用户名为:
<p>${userName}
</p>
-
你的密码为:
<p>${password}
</p>
-
<a href="login" onclick="Logout">退出
</a>
-
-
-
</body>
-
</html>
1、首先是登陆界面(注意看URL)
2、登录,界面的URL变了而且登陆的Controller存储的数据(用户名和密码)
先总结到这了
仅供个人学习使用!
SpringMVC就不用多介绍了,这篇文章总结一下URL重定向的数据保存问题,至于什么是URL重定向?在之前的博客中我有介绍过。