SpringMvcDay(02)

16 篇文章 1 订阅
5 篇文章 0 订阅

控制器向页面传递数据

上次课程中我们学习了怎么在控制器中接收表单提交过来的信息

下面的课程我们要学习怎么将控制器中的信息,发送到页面上显示

我们以下面的简单业务为例

一个登录页面,输入用户名和密码

根据登录成功或失败在一个页面上显示信息

利用Request对象实现传递数据

步骤1:创建登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post" action="handle_login.do">
    <div>
        <label>用户</label>
        <input type="text" name="username">
    </div>
    <div>
        <label>密码</label>
        <input type="password" name="password">
    </div>
    <div>
        <input type="submit" value="提交">
    </div>
</form>
</body>
</html>

步骤2:

编写一个控制器方法将这个页面显示出来

@RestController
@RequestMapping("/home")
public class HomeController {
    //显示登录页面的方法
    @GetMapping("/login.do")
    //localhost:8080/home/login.do
    public ModelAndView showLogin(){
        return new ModelAndView("login");
    }
}

步骤3:

在执行登录操作前,先准备好显示登录成功或失败信息的页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>消息</h1>
    <p th:text="${message}"></p>
</body>
</html>

步骤4:

编写控制器代码,判断登录成功或失败并给出相应信息

 //接收表单提交的登录信息判断登录结果,并返回信息
    // 由于我们要使用request对象传递信息,所以需要声明它
    @PostMapping("/handle_login.do")
    public ModelAndView handleLogin(
            String username, String password,
            HttpServletRequest request){
        //使用request.setAttribute()方法向页面传递信息
        //setAttribute(key,value)页面上出现的th:标签指定的内容
        //会对应setAttribute方法中的key,显示这个key对应的value
        if("tom".equals(username)){
            if("123".equals(password)){
                request.setAttribute("message","登录成功");
                return new ModelAndView("message");
            }else{
                request.setAttribute("message","密码错误");
                return new ModelAndView("message");
            }
        }else{
            //传递信息:message指的是页面中th:text指定的key
            request.setAttribute("message","用户名错误");
            //返回视图:这里的message指定的是message.html页面
            return new ModelAndView("message");
        }
    }

我们使用request对象进行了控制器到页面信息的传递

但是这样做使控制器耦合了ServletAPI

SpringMvc的封装就没有意义了,违背了SpringMvc的设计理念

而且不方便测试,所以实际开发中不推荐使用

利用ModelAndView传递数据

使用ModelAndView类提供的addObject(key,Value)方法

也能从控制器向页面传递数据

代码如下

//使用ModelAndView传递信息的方法
    @PostMapping("/handle_login.do")
    public ModelAndView handleLogin(
            String username,String password){

        if("tom".equals(username)){
            if("123".equals(password)){
                ModelAndView mv=new ModelAndView("message");
                mv.addObject("message","登录成功");
                return mv;
            }else{
                ModelAndView mv=new ModelAndView("message");
                mv.addObject("message","密码错误");
                return mv;
            }
        }else{
            //使用ModelAndView对象的addObject方法添加信息
            //信息格式也是键值对
            ModelAndView mv=new ModelAndView("message");
            mv.addObject("message","用户名错误");
            return mv;
        }

    }

上面代码的问题是一旦完全依赖ModelAndView那么在传递信息时就必须先实例化它的对象

如果一个控制中有不同页面的跳转,需要实例化多个ModelAndView对象,代码比较多

使用ModelMap向页面传递数据

在控制器的方法中声明ModelMap

DispatcherServlet会获得这个ModelMap中的信息

以便传递给页面视图显示

//使用ModelMap传递信息的方法
    @PostMapping("/handle_login.do")
    public ModelAndView handleLogin(
            String username, String password, ModelMap map){
        if("tom".equals(username)){
            if("123".equals(password)){
                map.put("message","登录成功");
                return  new ModelAndView("message");
            }else{
                map.put("message","密码错误");
                return  new ModelAndView("message");
            }
        }else{
            map.put("message","用户名错误");
            return  new ModelAndView("message");
        }
    }

这个方法是现在学习的三种方法中综合效果最好的

小结

1.request对象的传递方式:和ServletApi耦合,不好测试不推荐使用

2.ModelAndView:脱离了ServletApi但是代码比较多,可以使用

3.ModelMap:单独的一个map,只和DispatchServlet相关,综合效果好,推荐使用

习题:

1.创建一个页面,页面中有一个表单,表单中两个输入框 num1和num2两个数字

2.编写控制器方法显示这个页面

3.编写显示结果的页面使用Thymeleaf标签指定显示的位置

4.编写控制器方法处理表单的提交,使用ModelMap方法将num1和num2的和传递给页面

转发与重定向

控制器中实际上有两种使页面跳转的方式

分别是转发和重定向

什么是转发

我们上面学习的课程中使用的就都是转发来跳转页面的

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SAdpCeCw-1606999153678)(S1MmkyBKG3wb589p.png!thumbnail)]

转发全程请求转发

是服务器内部对一个请求的偏转和发送,是一次请求

浏览器地址栏显示的是控制器的路径,显示的内容却是页面模板的内容

什么是重定向

重定向实际上是http协议提供的功能

客户端向服务器发送请求后,服务器向客户端响应302状态码

浏览器接收到302状态码后,立即向服务器指定的url发送新的请求

这就是重定向的过程

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5FiGBAcM-1606999153681)(a0efy22E6ksxH4xp.png!thumbnail)]

上面图示我们编写一个控制器

控制器中将进行重定向操作

具体代码如下

/**
 * 演示重定向的控制器
 */
@RestController
@RequestMapping("/doc")
public class DocController {

    @GetMapping("/doc.do")
    public ModelAndView doc(){
        System.out.println("重定向到苍老师的网站");
        return new ModelAndView(
                "redirect:http://doc.canglaoshi.org");
    }

}

当我们输入url:localhost:8080/doc/doc.do时

我们看到了苍老师的网站

代码中我们返回的ModelAndView中

字符串以"redirect:"开头,DispatcherServlet在接收到这样的控制器返回结果时

就是进行重定向操作

什么时候使用重定向呢?

只要当前控制器的跳转目标不是我们的视图模板,就使用重定向

原因是SpringMvc中使用转发只能访问resources/templates/下的html文件

重定向和转发的区别

常见面试题:

1.请求次数上

转发过程中只出现一次请求

重定向过成中有两次请求

2.地址栏区别

转发后显示的是浏览器初始时访问的控制器路径

重定向显示的是重定向后新的请求发送给的路径

3.作用域区别

转发因为是一次请求,转发的页面和请求的路径共享同一个request的数据

重定向是两次请求,请求的页面和重定向的页面不能共享同一个request的数据

SpringMvc中使用Session对象

我们编写的vrd项目登录后将用户信息保存在Session中

需要验证用户是否登录时直接检查session对象就可以了

SpringMvc中怎么控制管理Session呢?

在登录成功时将用户对象保存在Session里

代码如下:

//使用ModelMap传递信息的方法
    @PostMapping("/handle_login.do")
    public ModelAndView handleLogin(
            String username, String password, ModelMap map,
            HttpSession session){
        //方法的参数中声明session对象
        //DispatcherServlet就会自动将当前会话对象赋值到这个session中
        if("tom".equals(username)){
            if("123".equals(password)){
                map.put("message","登录成功");
                //登录成功,将用户保存在Session中
                session.setAttribute("user",username);
                return  new ModelAndView("message");
            }else{
                map.put("message","密码错误");
                return  new ModelAndView("message");
            }
        }else{
            map.put("message","用户名错误");
            return  new ModelAndView("message");
        }
    }

上面的代码在控制的方法中声明的HttpSession类型的参数

会自动赋值Session对象

在登录成功时,将用户名保存在Session中

下面创建一个页面来显示session中用户的信息

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>这里显示Session中保存的用户名</h1>
    <p>欢迎您:<span th:text="${username}"></span></p>
</body>
</html>

这个页面是用来显示Session中的用户名的

还需要一个控制器来讲登录用户的信息传递到这个页面上

我们继续在HomeController中编写

代码如下

//显示已登录用户名的页面
    @GetMapping("/show_name.do")
    public ModelAndView showName(
            HttpSession session,ModelMap map){
        //先从session中获得用户名
        String username=(String)session.getAttribute("user");
        //再将获得的用户名赋值到ModelMap中用于显示在页面上
        map.put("username",username);
        //转发到视图
        return new ModelAndView("welcome");
    }

拦截器

什么是拦截器

是SpringMvc提供的组件

可以使得指定路径的请求被处理时,运行拦截器中的代码

而且拦截器中可以决定这个请求继续执行还是阻止执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zaUaYP8q-1606999153684)(372GmKyottgugtcE.png!thumbnail)]

为什么使用拦截器

上面的图片表示一个项目中有很多请求

有的请求可以直接放行,显示目标页面的内容

但是例如"我的账户"这样的请求必须先登录才能访问

而且这样先登录才能访问的页面不止一个

使用拦截器将所有需要登录才能访问的页面控制起来,方便权限管理

怎么使用拦截器

首先来编写一个拦截器,了解它最基本的使用

新建一个拦截器包

cn.tedu.interceptor

在这个包中新建一个拦截器类DemoInterceptor

代码如下

public class DemoInterceptor implements HandlerInterceptor {

    //这个方法的运行时机是进入到控制器之前
    //方法的返回值控制是否允许这个请求进入控制器
    //返回true表示放行,返回false表示阻止
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("preHandle运行");
        return true;
    }

    //控制器运行完毕之后运行
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("postHandler运行");
    }
    //在视图生成之后运行(浏览器还没有显示)
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        System.out.println("afterCompletion运行");
    }
}

上面定义的拦截器还没有注册到Spring 中

下面通过编写配置类代码来注册拦截器

SpringMvcConfig类代码如下

@ComponentScan("cn.tedu.controller")
//@EnableWebMvc 表示当前配置类可以配置SpringMvc相关信息
//这里就是值可以配置拦截器
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
    //上面实现的接口也是表示当前的SpringMvcConfig类是可以配置拦截器的

    //下面的方法就是专门注册拦截器到SpringMvc中的方法
    //参数InterceptorRegistry就是注册拦截器的对象
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册一个拦截器,拦截/home/show_name.do
        //即当访问/home/show_name.do路径时执行这个拦截器
        registry.addInterceptor(new DemoInterceptor())
                .addPathPatterns("/home/show_name.do");
    }
 	//省略原有的其它代码
}

使用拦截器实现登录控制

我们现在有需求:

必须先登录才能访问/home/show_name.do

否则直接跳转到登录页面

下面我们来编写这个需求的拦截器

AccessInterceptor,代码如下

public class AccessInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        HttpSession session = request.getSession();
        String username = (String) session.getAttribute("user");
        System.out.println("拦截器获得session中用户名为:" + username);
        //根据从session中获得的用户名决定当前请求是否可以继续访问目标控制器
        if (username == null || username.isEmpty()) {
            // 如果用户名是空,重定向到登录页
            //request.getContextPath()获得结果等价于
            //localhost:8080
            String path=request.getContextPath()
                    +"/home/login.do";
            response.sendRedirect(path);
            return false;
        }
        System.out.println("已经登录,放行!");
        return true;
    }
}

在配置类中注册这个拦截器

 @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册一个拦截器,拦截/home/show_name.do
        //即当访问/home/show_name.do路径时执行这个拦截器
        registry.addInterceptor(new DemoInterceptor())
                .addPathPatterns("/home/show_name.do");
        //再注册一个拦截器
        registry.addInterceptor(new AccessInterceptor())
                .addPathPatterns("/home/show_name.do");

    }

运行测试…

拦截器的详细配置

一个项目可能又很多url都需要先登录后才能访问

怎么配置拦截器拦截多个url?

代码如下

registry.addInterceptor(new AccessInterceptor())
                .addPathPatterns(
                        "/home/show_name.do",
                        "/user/get.do",
                        "/doc/doc.do",
                        "/doc/chong.do");

addPathPatterns允许配置多个路径设置拦截

如果大型项目需要拦截的路径很多,写起来会很麻烦

所以拦截器的路径配置支持使用*通配

代码如下

//再注册一个拦截器
        registry.addInterceptor(new AccessInterceptor())
                .addPathPatterns(
                        "/home/show_name.do",
                        "/user/get.do",
                        "/doc/*",
                        "/cart/**");
         /*
         /doc/* 的配置可以拦截 /doc/chong.do /doc/doc.do
         但是如果今后开发出现了类似   /doc/xxx/abc.do的多级目录
          /doc/*是不能拦截的
         只能使用/doc/** 这种写法 它可以拦截/doc/开头的所有请求
         */

使用通配设置拦截路径非常爽

但是如果这个路径下有个别请求需要单独放行,那么不好设置了

没关系,SpringMvc支持拦截器设置例外

即使在通配范围内的路径也可以访问

代码如下

registry.addInterceptor(new AccessInterceptor())
                .addPathPatterns(
                        "/home/show_name.do",
                        "/user/get.do",
                        "/doc/*",
                        "/cart/**")
                .excludePathPatterns(
                        "/doc/chong.do",
                        "/cart/add.do");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值