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
  2. 在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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容