zoukankan      html  css  js  c++  java
  • 4、SpringMVC 转发&重定向&异常处理&拦截器

    学习资源:动力节点的2020最新SpringMVC教程【IDEA版】-springmvc从入门到精通


    1、请求转发与重定向

    当控制器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。框架默认采用重定向的方式。

    而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它控制器。

    注意,对于请求转发的页面,可以是 WEB-INF中页面;而重定向的页面,是不能为WEB-INF中页的,因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。

    image-20200905164313422

    SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。

    • forward:表示转发,实现 request.getRequestDispatcher("xx.jsp").forward()
    • redirect:表示重定向,实现 response.sendRedirect("xxx.jsp")

    共同点:

    • forward 和 redirect 都不会与视图解析器一同工作,这样可以在配置了视图解析器同时指定其他不同位置的视图。
    • forward 和 redirect 视图时,都要写出视图相对于项目根的路径
    • forward 和 redirect 都可以访问视图文件,也可以访问其他的控制器。
    • 控制器方法返回 ModelAndView、String、void 都可以使用forward,redirect。

    1.1、请求转发

    RequestMapping(value="/doForward")
    public Mode1AndView doForward(Integer age,String name){
        
        Mode1AndView mv = new Mode1AndView();
        mv.addobject("myname", name);
        mv.addobject("myage", age);
        
        // 转发到 WEB-INF 下的视图
        // mv.setViewName("forward:/WEB-INF/view/show.jsp");
        
        // 转发到根目录下的视图
        mv.setViewName("forward:/other.jsp");
        
        // 转发到其他控制器
        mv.setViewName("forward:/doSome");
        return mv;
    }
    
    RequestMapping(value="/doSome")
    public String doSome(Integer age,String name){
        
    }
    

    1.2、请求重定向

    @RequestMapping(value = " /doredirect.do")
    public ModelAndView doRedirect(String name,Integer age){
    
        ModelAndView mv = new ModelAndView();
        
        //重定向不能访问受保护的WEB-INF下面的资源
        //mv.setViewName ( "redirect:/WEB-INF/view/show.jsp");
        
        // 重定向到视图
        mv.setViewName ("redirect:/other.jsp");
        
    	// 重定向到其他控制器
        mv.setViewName("forward:/doSome");
        return mv;
    }
    
    RequestMapping(value="/doSome")
    public String doSome(Integer age,String name){
        
    }
    

    2、异常处理

    SpringMVC 框架采用的是统一的、全局的异常处理,把 controller 中的所有异常处理集中到一个地方集中处理,采用的是 aop 的思想,把业务逻辑和异常处理的代码分开,实现了解耦合。

    框架对异常处理的实现步骤:

    1. 自定义 Exception 类
      控制器中发生的异常,它可能是已知类型的,如输入的参数不符合要求,对于已知类型的错误,我们可以并在适当的时机在控制器中主动抛出这个类型异常。

    2. 控制器抛出异常,方法上使用 @ExceptionHandler
      在可能抛出异常的(自定义类型+未知类型)控制器方法上使用,该注解只有一个可选属性 value,为一个 Class<?> 数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
      被注解的方法,其返回值可以是 ModelAndViewString ,或 void ,方法名随意,方法参数可以是 Exception 及其子类对象、 HttpServletRequestHttpServletResponse 等,系统会自动为这些方法参数赋值。

    3. 创建全局异常处理类

      1. 类的上面使用 @ControllerAdvice
      2. 类中定义处理特定类型异常的方法,方法的上面使用 @ExceptionHandler(value=异常的类型) ,表示当控制器发生此类型的异常时,由当前的方法处理。
        1. 处理异常的方法,其返回值可以是 ModelAndViewString ,或 void ,方法名随意,方法参数可以是 Exception 及其子类对象、 HttpServletRequestHttpServletResponse 等,系统会自动为这些方法参数赋值。
        2. 异常的处理逻辑是:
          1. 记录异常的信息,到数据库、日志文件等
          2. 发送通知,将异常信息通过邮件、短信等形式发送给相关人员
          3. 在页面给用户发生错误的友好提示
      3. 类中再定义处理其他异常的方法,该方法的上面,直接使用 @ExceptionHandler() 即可
    4. 注册组件扫描器和注解驱动

      1. 注册 @Controller 扫描器
      <context:component-scan base-package="com.chen.controller"
      
      1. 注册 @ControllerAdvice 扫描器
      <context:component-scan base-package="com.chen.exceptionHandler"
      
      1. 注解驱动
      <mvc:annotation-driven/>
      

    2.1、自定义异常类

    定义三个异常类: NameExceptionAgeExceptionMyUserException ,其中 MyUserException 是另外两个异常的父类。

    package com.chen.exception;
    
    public class MyException extends Exception{
    
        public MyException() {
            super();
        }
    
        public MyException(String message) {
            super(message);
        }
    }
    
    package com.chen.exception;
    
    public class NameException extends MyException {
        public NameException() {
            super();
        }
    
        public NameException(String message) {
            super(message);
        }
    }
    
    package com.chen.exception;
    
    public class AgeException extends MyException {
        public AgeException() {
            super();
        }
    
        public AgeException(String message) {
            super(message);
        }
    }
    

    2.2、controller 抛出异常

    @Controller
    @RequestMapping("/student")
    public class StudentController {    
        
    	@RequestMapping(value="/register")
        public ModelAndView doRegister(Integer age,String name) throws MyException {
    
            ModelAndView mv = new ModelAndView();
    
            if(! "张三".equals(name)){
                throw new NameException("不收张三!!!");
            }
            else if(age==null || age > 80){
                throw new AgeException("年龄太大了,不收!!!");
            }
            mv.addObject( "myname", name);
            mv.addObject( "myage",age);
            mv.setViewName( "show" );
            return mv;
        }
    }
    

    2.3、创建全局异常处理类

    package com.chen.exceptionHandler;
    
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        //处理NameException的异常。
        @ExceptionHandler(value = NameException.class)
        public ModelAndView doNameException(Exception exception){
    
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","姓名必须是zs,其它用户不能访问");
            mv.addObject("ex",exception);
            mv.setViewName("nameError");
            return mv;
        }
    
        //处理AgeException
        @ExceptionHandler(value = AgeException.class)
        public ModelAndView doAgeException(Exception exception){
    
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","你的年龄不能大于80");
            mv.addObject("ex",exception);
            mv.setViewName("ageError");
            return mv;
        }
    
        //处理其它异常, NameException, AgeException以外,不知类型的异常
        @ExceptionHandler
        public ModelAndView doOtherException(Exception exception){
    
            //处理其它异常
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","你的年龄不能大于80");
            mv.addObject("ex",exception);
            mv.setViewName("otherError");
            return mv;
        }
    }
    

    2.4、定义异常响应页面

    <%--nameError.jsp--%>
    <body>
    nameErrors page<br>
    <hr>
    ${ex.message }<br>
    </body>
    
    <%--ageError.jsp--%>
    <body>
    ageError page<br>
    <hr>
    ${ex.message }<br>
    </body>
    
    <%--otherError.jsp--%>
    <body>
    otherError page<br>
    <hr>
    ${ex.message }<br>
    </body>
    

    2.5、配置 springmvc 配置文件

    <context:component-scan base-package="com.chen.controller"/>
    
    <context:component-scan base-package="com.chen.exceptionHandler"/>
    <mvc:annotation-driven/>
    

    3、拦截器

    SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求, 并进行相应的预处理与后处理。 其拦截的时间点在 “ 控制器映射器根据用户提交的请求映射出了所要执行的控制器类, 并且也找到了要执行该控制器类的控制器适配器,在控制器适配器执行控制器之前 ” 。当然,在控制器映射器映射出所要执行的控制器类时,已经将拦截器与控制器组合为了一个控制器执行链,并返回给了中央调度器。

    拦截器的作用域是全局的,可以对多个控制器做拦截。一个项目中可以有 0 个或多个拦截器

    拦截器常用于:用户登陆检查、用户权限检查、记录日志等。

    拦截器的工作时间:

    1. 在 controller 执行前
    2. 在 controller 执行之后
    3. 在请求处理完成后

    3.1、一个拦截器

    拦截器的使用步骤:

    1. 自定义拦截器类,实现 HandlerInterceptor 接口,有选择地实现接口中的 3 个方法

      1. preHandle

        参数:

        • request
        • response
        • handler: 被拦截的控制器对象

        返回值:

        • true:请求通过了 preHandle 的验证,可以执行控制器方法。且会将 afterCompletion() 方法放入到一个专门的方法栈中等待执行
        • false:请求没有通过 preHandle 的验证,请求到达 preHandle 就截止了,没有被继续处理

        特点:

        • preHandle 在控制器方法执行之前执行,用户的请求首先到达此方法,是整个项目某些请求的入口。
        • 在 preHandle 中可以获取请求的信息, 可用于验证请求是否符合要求
          如,验证用户是否登录, 验证用户是否有权限访问某个连接地址(url),如果验证失败,可以截断请求,请求不能被处理;如果验证成功,可以放行请求,此时控制器方法才能执行
      2. postHandle
        参数:

        • request
        • response
        • handler
        • modelAndView:控制器方法返回的 视图+数据

        特点:

        • 在控制器方法执行之后执行,若控制器方法最终未被执行,则 postHandle 不会执行
        • 可以获取请求的信息
        • 能够获取到控制器方法的返回值 ModelAndView ,可以修改 ModelAndView 中的 数据+视图,可以影响到最后的请求处理结果,用于修正控制器的处理结果
      3. afterCompletion
        参数:

        • request
        • response
        • handler
        • exception:程序中发生的异常

        特点:

        • 当 preHandle() 方法返回 true 时,会将该方法放到专门的方法栈中,等到请求处理完成之后执行执行该方法(框架中规定对视图执行了 forward 就认定请求处理完成)。
          该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。
        • 一般用做资源回收工作, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收
    package com.chen.handler;
    
    public class MyInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, 
                                 HttpServletResponse response, Object handler) throws Exception {
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                    Object handler, Exception ex) throws Exception {
    
        }
    }
    
    1. springmvc 配置文件中声明拦截器,指定拦截的请求 uri 地址,/** 表示拦截所有请求
    <context:component-scan base-package="com.chen.*"/>
    
    <!-- 注册拦截器,可以有多个 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 
    		拦截指定路径的请求
    		<mvc:mapping path="/user/**"/> 
    		-->
            <!-- 拦截所有请求 -->
            <mvc:mapping path="/**"/>
            <!-- 所拦截请求使用的拦截器对象 -->
            <bean class="com.chen.handler.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

    image-20200906105717088


    3.2、多个拦截器

    package com.chen.handler;
    
    public class MyInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, 
                                 HttpServletResponse response, Object handler) throws Exception {
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                    Object handler, Exception ex) throws Exception {
    
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////
    public class MyInterceptor2 implements HandlerInterceptor {
    
    }
    
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/user/**"/>
            <bean class="com.chen.handler.MyInterceptor"/>
        </mvc:interceptor>
    	<mvc:interceptor>
            <mvc:mapping path="/student/**"/>
            <bean class="com.chen.handler.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

    image-20200906114038239


    3.3、拦截器与过滤器的区别

    1. 过滤器是 servlet 中的对象,拦截器是框架中的对象

    2. 过滤器是实现 Filter 接口的对象, 拦截器是实现 HandlerInterceptor 接口的对象

    3. 过滤器是用来设置request,response的参数、属性的,侧重数据过滤
      而拦截器是用来验证请求的,能截断请求

    4. 过滤器是在拦截器之前先执行的。

    5. 过滤器是 Tomcat 服务器创建的对象

      拦截器是 SpringMVC 容器中创建的对象

    6. 过滤器只有 1 个执行时间点

      拦截器有 3 个执行时间点

    7. 过滤器可以处理 jsp, js , html 等

      拦截器是侧重拦截对 Controller 的请求,如果请求不能被 DispatcherServlet 接收, 这个请求不会被拦截

    8. 拦截器拦截控制器方法的执行,过滤器过滤 servlet 的请求响应


    3.4、拦截器使用实例——验证用户是否登录

    index.jsp 发起请求,controller 处理请求,

    3.4.1、首页发起请求

    在首页 index.jsp 发起请求,携带用户信息:用户名、密码等。

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%
        String basePath = request.getScheme() + "://" +
                request.getServerName() + ":" + request.getServerPort() +
                request.getContextPath() + "/";
    %>
    <html>
        <head>
            <title>首页</title>
            <base href="<%=basePath%>">
        </head>
        <body>
            <h1>登录页面</h1>
            <hr>
            <form action="user/login">
                用户名:<input type="text" name="username"> <br>
                密码: <input type="password" name="pwd"> <br>
                <input type="submit" value="提交">
            </form>
        </body>
    </html>
    

    3.4.2、控制器

    package com.chen.controller;
    
    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        // 处理登陆请求
        @RequestMapping("/login")
        public String login(HttpSession session, String username, String pwd) throws Exception {
            // 向session记录用户身份信息
            session.setAttribute("user", username);
            return "success";
        }
        
    	// 没有登陆,就跳转到登陆页面
        @RequestMapping("/toLogin")
        public String jumpLogin() throws Exception {
            return "forward:/index";
        }
    
        // 登陆成功,就跳转到成功页面
        @RequestMapping("/toSuccess")
        public String jumpSuccess() throws Exception {
            return "success";
        }
    
        // 退出登陆,销毁 session ,跳转到登录页面
        @RequestMapping("logout")
        public String logout(HttpSession session) throws Exception {
            // session 过期
            session.invalidate();
            return "forward:/login";
        }
    }
    

    3.4.3、登陆成功页面

    登录成功页面 success.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%
        String basePath = request.getScheme() + "://" +
                request.getServerName() + ":" + request.getServerPort() +
                request.getContextPath() + "/";
    %>
    <html>
        <head>
            <title>登录成功</title>
            <base href="<%=basePath%>">
        </head>
        <body>
            <h1>登录成功页面</h1>
            <hr>
            ${user}
            <a href="user/logout">注销</a>
        </body>
    </html>
    

    3.4.4、拦截器

    package com.chen.handler;
    
    public class LoginInterceptor implements HandlerInterceptor {
    
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
                                 Object handler) throws ServletException, IOException {
            
            // 如果是登陆页面则放行
            System.out.println("uri: " + request.getRequestURI());
            if (request.getRequestURI().contains("login")) {
                return true;
            }
    
            HttpSession session = request.getSession();
            // 如果用户已登陆也放行
            if(session.getAttribute("user") != null) {
                return true;
            }
    
            // 用户没有登陆则跳转到登陆页面
            request.getRequestDispatcher("/index.jsp").forward(request, response);
            return false;
        }
    }
    

    3.4.5、注册拦截器

    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 拦截所有请求 -->
            <mvc:mapping path="/**"/>
            <!-- 所拦截请求使用的拦截器对象 -->
            <bean class="com.chen.handler.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  • 相关阅读:
    http协议详解
    001http协议
    django整合版
    数据库整合版
    并发编程整合版
    网络编程整合版
    面向对象编程
    LeetCode OJ:Binary Tree Postorder Traversal(后序遍历二叉树)
    LeetCode OJ:Populating Next Right Pointers in Each Node II(指出每一个节点的下一个右侧节点II)
    LeetCode OJ:Populating Next Right Pointers in Each Node(指出每一个节点的下一个右侧节点)
  • 原文地址:https://www.cnblogs.com/sout-ch233/p/13622449.html
Copyright © 2011-2022 走看看