梦蓝樱飞(一点一滴, 记录个人成长之路)

时光荏苒,祝愿勿忘曾经的初心 && 活在当下,一定做好现在的事情

SSM框架系列学习总结10之SpringMVC 拦截器&异常处理器

SpringMVC的拦截器实现权限的验证

拦截器拦截请求,然后再判断相关信息,是否请求可以继续下去

Springmvc如何实现拦截器功能:
只需要自定义一个类, 去实现springmvc提供的一个接口,HandlerInterceptor
image.png

/**
 * 自定义拦截器
 * Author menglanyingfei
 * Created on 2018.01.25 9:41
 */
public class InterceptorDemo1 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        /**
         * 在执行Handler方法前调用该方法, 真正实现拦截的方法
         * return true: 表示将该请求放行
         * return false: 表示不让请求继续往下执行
         */
        System.out.println("InterceptorDemo1 拦截请求! preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        /**
         * 在进入Handler以后, 返回ModelAndView之前执行
         */
        System.out.println("InterceptorDemo1 postHandle ");
    }

    /**
     * 该方法是在Handler执行完毕以后再调用该方法
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param e
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("InterceptorDemo1 afterCompletion");
    }
}

然后只要在springmvc.xml文件中配置一个拦截器,配置具体拦截的请求url

    <!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- path: 指定将要拦截的路径
            /**: 表示拦截所有请求
            class: 自定义拦截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.InterceptorDemo1"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- path: 指定将要拦截的路径
            /**: 表示拦截所有请求
            class: 自定义拦截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.InterceptorDemo2"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- path: 指定将要拦截的路径
            /**: 表示拦截所有请求
            class: 自定义拦截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
拦截器链的执行顺序:

SpringMVC中拦截器.png

Interceptor1 —-> interceptor2 —-> intercptor3 …
1. 如果两个拦截器都放行:
拦截的方法是根据配置文件的配置顺序进行拦截。其他方法相反
image.png

2.如果Interceptor1放行, interceptor2不放行
要执行Controller的方法, 一定要所有的拦截器都放行才能执行
image.png

3.如果两个Interceptor都返回false
image.png

示例演示: 实现用户登录权限的校验

  1. 拦截器拦截所有的请求
  2. 判断session中是否存在用户信息,如果存在则放行,如果不存在,接续判断请求路径是否是登录页面
  3. 如果请求路径是登录页面,那么就放行,如果不是, 则重定向到登录页面进行登录
定义登录的Controller
@Controller
public class UserController {

    @RequestMapping("/login.do")
    public String login(HttpSession session, String username, String password) {
        if (username != null && !"".equals(username)) {
            // 将用户信息保存到session中
            session.setAttribute("username", username);
            return "redirect:/findItemsByName.do";
        } else {
            return "forward:/jsp/login.jsp";
        }

    }

    @RequestMapping("/exit.do")
    public String exit(HttpSession session) {
        // 清除session
        session.invalidate();

        return "redirect:/jsp/login.jsp";
    }
}
定义拦截器
public class LoginInterceptor implements HandlerInterceptor {
    // 拦截所有请求
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        // 判断session中是否存在用户信息
        HttpSession session = request.getSession();
        // 获取session中的用户信息
        String username = (String) session.getAttribute("username");
        if (username != null) {
            return true; // 放行
        }
        // 获取请求路径
        String path = request.getRequestURI();
        if (path.contains("/login.do")) {
            return true;
        }
        // 重定向到登录页面
//        request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
        response.sendRedirect(request.getContextPath() + "/jsp/login.jsp");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
配置拦截器
   <mvc:interceptors>     
        <mvc:interceptor>
            <!-- path: 指定将要拦截的路径
            /**: 表示拦截所有请求
            class: 自定义拦截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

SpringMVC参数校验

使用Hibernate-validator对controller绑定的POJO形参进行校验。
1. 导入校验所需要的jar包
image.png

  1. 在xml文件中配置校验器
    <!-- 配置参数校验器 -->
    <bean id="validator"
          class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <!-- 校验器-->
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />

        <!-- 指定校验使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource" />
    </bean>

    <!-- 校验错误信息配置文件 -->
    <bean id="messageSource"
          class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <!-- 资源文件名-->
        <property name="basenames">
            <list>
                <value>classpath:CustomValidationMessages</value>
            </list>
        </property>
        <!-- 资源文件编码格式 -->
        <property name="fileEncodings" value="utf-8" />
        <!-- 对资源文件内容缓存时间,单位秒 -->
        <property name="cacheSeconds" value="120" />
    </bean>

3.引入校验器:

    <!-- 配置注解形式的适配器和映射器
        conversion-service: 配置自定义参数绑定器
        validator: 引入一个校验器
    -->
    <mvc:annotation-driven conversion-service="conversionService"
        validator="validator"/>

错误信息属性文件:CustomValidationMessages.properties

#配置错误提示信息
name.length.error=商品名称过长或者过短!!!
date.is.null=date can't be null

4.在pojo属性中添加参数的校验

public class Items {

    private Integer id;
    @Size(min = 2, max = 15, message = "{name.length.error}")
    private String name;
    private Double price;
    private String detail;
    private String pic;
    @NotNull(message = "{date.is.null}")
    private Date createtime;
    // ...
}
  1. Controller方法中的代码
    // 修改信息
    @RequestMapping(value = "/updateItems.do")
    public String updateItems(Model model,
                              @Validated Items items, BindingResult bindingResult) throws Exception {

        if (bindingResult.hasErrors()) {
            // 获取错误的信息
            List<ObjectError> errorList = bindingResult.getAllErrors();
            // 将错误信息保存到request域中
//            errorList.get(0).getDefaultMessage();
            model.addAttribute("errorList", errorList);
            // 转发到商品修改页面
            return "forward:/jsp/editItem.jsp";
        }

        itemsService.updateItems(items);
        return "redirect:/findItemsByName.do";

    }

image.png
如果添加了参数校验, 那么必须在pojo参数前面加上@Validated注解,然后在参数后面加上BindingResult 参数, 用来获取错误的信息

数据的回写

比如在商品修改页面 提交以后 如果出现错误 那么重新回到商品修改页面的时候,商品的信息还在 这就是数据的回写

默认情况下,如果出现异常,那么会自动将pojo的类型名 并且首字母小写作为request域的键,然后将pojo的对象作为值, 然后传递到页面。

如果在页面接收值的时候使用的变量名不是pojo的类型首字母小写,那么在POJO参数的前面加上@ModelAttribute(“items1”) 明确指定 request域中的键为items1,那么这个时候就可以在页面上 通过items1来获取值

    // 修改信息
    @RequestMapping(value = "/updateItems.do")
    public String updateItems(Model model,
                              @Validated @ModelAttribute("items1") Items items, BindingResult bindingResult ) throws Exception {

如果上面的方式不能理解,那么可以手动回写: 加上如下代码即可
image.png
如果是简单类型的参数回写:那么只能通过
model.addAttribute("key", value);

Springmvc的全局异常处理

  1. 自定义一个异常类
/**
 * 自定义异常类
 * Author menglanyingfei
 * Created on 2018.01.25 16:37
 */
public class MyException extends Exception {
    // 保存错误信息
    private String message;

    public MyException(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
  1. 定义一个类实现HandlerExceptionResolver接口
/**
 * 这个异常解析器会在遇到异常的时候
 * 会被处理器适配器来调用
 * Author menglanyingfei
 * Created on 2018.01.25 16:40
 */
public class MyExceptionResolver implements HandlerExceptionResolver {
    /*
        判断异常的信息: 如果是我们自定义的异常就直接获取异常信息
        然后跳转到错误页面:
        如果不是自定义异常, 那么给出一个未知错误的提示
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView mv = new ModelAndView();
        MyException me = null;
        // 判断捕获到的异常是否为自定义异常
        if (e instanceof MyException) {
            // 将异常强转为MyException类型
            me = (MyException) e;
            // 获取异常信息
            String errorMessage = me.getMessage();
            mv.addObject("errorMessage", errorMessage);

        } else {
            mv.addObject("errorMessage", "未知错误, 请于管理员联系!");
        }
        // 这里配置了视图解析器
        mv.setViewName("/error");
        return mv;
    }
}

3.在xml文件中配置异常解析器:

    <!-- 配置异常解析器 -->
    <bean class="com.wtu.ssm.exception.MyExceptionResolver"/>

错误页面error.jsp:

<%--
  User: menglanyingfei
  Date: 2018/1/25
  Time: 16:50
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>错误提示</title>
</head>
<body>
    ${errorMessage}
</body>
</html>

关于项目中异常处理注意的问题:
1. 如果使用springmvc Mapper的异常抛给service ,service的异常抛给controller
2. 开发阶段不需要将异常处理掉
3. 在controller也是将异常给抛出去,那么会由处理器适配器捕获异常调用我们自己编写的异常解析器进行异常的处理。

完整代码地址

https://github.com/menglanyingfei/SSMLearning/tree/master/jar%E5%8C%85
https://github.com/menglanyingfei/SSMLearning/tree/master/SSM

阅读更多
版权声明:本文为 梦蓝樱飞 原创文章,可以随意转载,但真诚希望在明确位置注明原文超链接的出处!!! 非常感谢! https://blog.csdn.net/menglanyingfei/article/details/79275654
个人分类: SSM
所属专栏: SSM框架系列学习总结
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

SSM框架系列学习总结10之SpringMVC 拦截器&异常处理器

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭