SpringMVC【校验器、统一处理异常、RESTful、拦截器】(下)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本博文主要讲解的知识点如下: 校验器 统一处理异常 RESTful 拦截器

定义统一异常处理器类


public class CustomExceptionResolver implements HandlerExceptionResolver  {
    //前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常就会执行此方法
    //handler最终要执行的Handler,它的真实身份是HandlerMethod
    //Exception ex就是接收到异常信息
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        //输出异常
        ex.printStackTrace();
        //统一异常处理代码
        //针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面展示
        //异常信息
        String message = null;
        CustomException customException = null;
        //如果ex是系统 自定义的异常,直接取出异常信息
        if(ex instanceof CustomException){
            customException = (CustomException)ex;
        }else{
            //针对非CustomException异常,对这类重新构造成一个CustomException,异常信息为“未知错误”
            customException = new CustomException("未知错误");
        }
        //错误 信息
        message = customException.getMessage();
        request.setAttribute("message", message);
        try {
            //转向到错误 页面
            request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);
        } catch (ServletException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return new ModelAndView();
    }
}


配置统一异常处理器


<!-- 定义统一异常处理器 -->
    <bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean>

31.jpg

这里写图片描述


RESTful支持


我们在学习webservice的时候可能就听过RESTful这么一个名词,当时候与SOAP进行对比的…那么RESTful究竟是什么东东呢???

RESTful(Representational State Transfer)软件开发理念,RESTful对http进行非常好的诠释

如果一个架构支持RESTful,那么就称它为RESTful架构…

以下的文章供我们了解:

http://www.ruanyifeng.com/blog/2011/09/restful

综合上面的解释,我们总结一下什么是RESTful架构:

  •   (1)每一个URI代表一种资源;
  •   (2)客户端和服务器之间,传递这种资源的某种表现层;
  •   (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"

关于RESTful幂等性的理解:http://www.oschina.net/translate/put-or-post

简单来说,如果对象在请求的过程中会发生变化(以Java为例子,属性被修改了),那么此是非幂等的。多次重复请求,结果还是不变的话,那么就是幂等的。

PUT用于幂等请求,因此在更新的时候把所有的属性都写完整,那么多次请求后,我们其他属性是不会变的

在上边的文章中,幂等被翻译成“状态统一性”。这就更好地理解了。

其实一般的架构并不能完全支持RESTful的,因此,只要我们的系统支持RESTful的某些功能,我们一般就称作为支持RESTful架构


url的RESTful实现


非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&….

RESTful的url是简洁的:http:// localhost:8080/items/editItems/1


更改DispatcherServlet的配置


从上面我们可以发现,url并没有.action后缀的,因此我们要修改核心分配器的配置


<!-- restful的配置 -->
    <servlet>
        <servlet-name>springmvc_rest</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 加载springmvc配置 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml"即:springmvc-serlvet.xml -->
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc_rest</servlet-name>
        <!-- rest方式配置为/ -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>


在Controller上使用PathVariable注解来绑定对应的参数


//根据商品id查看商品信息rest接口
    //@RequestMapping中指定restful方式的url中的参数,参数需要用{}包起来
    //@PathVariable将url中的{}包起参数和形参进行绑定
    @RequestMapping("/viewItems/{id}")
    public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{
        //调用 service查询商品信息
        ItemsCustom itemsCustom = itemsService.findItemsById(id);
        return itemsCustom;
    }


当DispatcherServlet拦截/开头的所有请求,对静态资源的访问就报错:我们需要配置对静态资源的解析


<!-- 静态资源 解析 -->
    <mvc:resources location="/js/" mapping="/js/**" />
    <mvc:resources location="/img/" mapping="/img/**" />

/**就表示不管有多少层,都对其进行解析,/*代表的是当前层的所有资源..



SpringMVC拦截器


在Struts2中拦截器就是我们当时的核心,原来在SpringMVC中也是有拦截器的

用户请求到DispatherServlet中,DispatherServlet调用HandlerMapping查找Handler,HandlerMapping返回一个拦截的链儿(多个拦截),springmvc中的拦截器是通过HandlerMapping发起的。

实现拦截器的接口:


public class HandlerInterceptor1 implements HandlerInterceptor {
    //在执行handler之前来执行的
    //用于用户认证校验、用户权限校验
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("HandlerInterceptor1...preHandle");
        //如果返回false表示拦截不继续执行handler,如果返回true表示放行
        return false;
    }
    //在执行handler返回modelAndView之前来执行
    //如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor1...postHandle");
    }
    //执行handler之后执行此方法
    //作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
    //实现 系统 统一日志记录
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("HandlerInterceptor1...afterCompletion");
    }
}


配置拦截器


<!--拦截器 -->
    <mvc:interceptors>
        <!--多个拦截器,顺序执行 -->
        <!-- <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor1"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor2"></bean>
        </mvc:interceptor> -->
        <mvc:interceptor>
            <!-- /**可以拦截路径不管多少层 -->
            <mvc:mapping path="/**" />
            <bean class="cn.itcast.ssm.controller.interceptor.LoginInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>


测试执行顺序


如果两个拦截器都放行


测试结果:
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle
HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
总结:
执行preHandle是顺序执行。
执行postHandle、afterCompletion是倒序执行


1 号放行和2号不放行


测试结果:
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
总结:
如果preHandle不放行,postHandle、afterCompletion都不执行。
只要有一个拦截器不放行,controller不能执行完成


1 号不放行和2号不放行


测试结果:
HandlerInterceptor1...preHandle
总结:
只有前边的拦截器preHandle方法放行,下边的拦截器的preHandle才执行。


日志拦截器或异常拦截器要求

  • 将日志拦截器或异常拦截器放在拦截器链儿中第一个位置,且preHandle方法放行


拦截器应用-身份认证


拦截器拦截


public class LoginInterceptor implements HandlerInterceptor {
    //在执行handler之前来执行的
    //用于用户认证校验、用户权限校验
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        //得到请求的url
        String url = request.getRequestURI();
        //判断是否是公开 地址
        //实际开发中需要公开 地址配置在配置文件中
        //...
        if(url.indexOf("login.action")>=0){
            //如果是公开 地址则放行
            return true;
        }
        //判断用户身份在session中是否存在
        HttpSession session = request.getSession();
        String usercode = (String) session.getAttribute("usercode");
        //如果用户身份在session中存在放行
        if(usercode!=null){
            return true;
        }
        //执行到这里拦截,跳转到登陆页面,用户进行身份认证
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        //如果返回false表示拦截不继续执行handler,如果返回true表示放行
        return false;
    }
    //在执行handler返回modelAndView之前来执行
    //如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor1...postHandle");
    }
    //执行handler之后执行此方法
    //作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
    //实现 系统 统一日志记录
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("HandlerInterceptor1...afterCompletion");
    }
}


Controller


@Controller
public class LoginController {
    //用户登陆提交方法
    @RequestMapping("/login")
    public String login(HttpSession session, String usercode,String password)throws Exception{
        //调用service校验用户账号和密码的正确性
        //..
        //如果service校验通过,将用户身份记录到session
        session.setAttribute("usercode", usercode);
        //重定向到商品查询页面
        return "redirect:/items/queryItems.action";
    }
    //用户退出
    @RequestMapping("/logout")
    public String logout(HttpSession session)throws Exception{
        //session失效
        session.invalidate();
        //重定向到商品查询页面
        return "redirect:/items/queryItems.action";
    }
}


总结


  • 使用Spring的校验方式就是将要校验的属性前边加上注解声明。
  • 在Controller中的方法参数上加上@Validation注解。那么SpringMVC内部就会帮我们对其进行处理(创建对应的bean,加载配置文件)
  • BindingResult可以拿到我们校验错误的提示
  • 分组校验就是将让我们的校验更加灵活:某方法需要校验这个属性,而某方法不用校验该属性。我们就可以使用分组校验了。
  • 对于处理异常,SpringMVC是用一个统一的异常处理器类的。实现了HandlerExceptionResolver接口。
  • 对模块细分多个异常类,都交由我们的统一异常处理器类进行处理。
  • 对于RESTful规范,我们可以使用SpringMVC简单地支持的。将SpringMVC的拦截.action改成是任意的。同时,如果是静态的资源文件,我们应该设置不拦截。
  • 对于url上的参数,我们可以使用@PathVariable将url中的{}包起参数和形参进行绑定
  • SpringMVC的拦截器和Struts2的拦截器差不多。不过SpringMVC的拦截器配置起来比Struts2的要简单。
  • 至于他们的拦截器链的调用顺序,和Filter的是没有差别的。
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
14天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
3月前
|
JSON 前端开发 Java
spring mvc Rest风格
spring mvc Rest风格
32 0
|
1天前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
1月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
1月前
|
XML JSON 数据库
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
这篇文章详细介绍了RESTful的概念、实现方式,以及如何在SpringMVC中使用HiddenHttpMethodFilter来处理PUT和DELETE请求,并通过具体代码案例分析了RESTful的使用。
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
|
1月前
|
前端开发 应用服务中间件 数据库
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
这篇文章通过一个具体的项目案例,详细讲解了如何使用SpringMVC、Thymeleaf、Bootstrap以及RESTful风格接口来实现员工信息的增删改查功能。文章提供了项目结构、配置文件、控制器、数据访问对象、实体类和前端页面的完整源码,并展示了实现效果的截图。项目的目的是锻炼使用RESTful风格的接口开发,虽然数据是假数据并未连接数据库,但提供了一个很好的实践机会。文章最后强调了这一章节主要是为了练习RESTful,其他方面暂不考虑。
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
|
1月前
|
JSON 前端开发 Java
Spring MVC返回JSON数据
综上所述,Spring MVC提供了灵活、强大的方式来支持返回JSON数据,从直接使用 `@ResponseBody`及 `@RestController`注解,到通过配置消息转换器和异常处理器,开发人员可以根据具体需求选择合适的实现方式。
94 4
|
1月前
|
XML 前端开发 Java
Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)
Spring MVC提供了灵活多样的参数接收方式,可以满足各种不同场景下的需求。了解并熟练运用这些基本的参数接收技巧,可以使得Web应用的开发更加方便、高效。同时,也是提高代码的可读性和维护性的关键所在。在实际开发过程中,根据具体需求选择最合适的参数接收方式,能够有效提升开发效率和应用性能。
83 3
|
1月前
|
XML 前端开发 Java
Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)
Spring MVC提供了灵活多样的参数接收方式,可以满足各种不同场景下的需求。了解并熟练运用这些基本的参数接收技巧,可以使得Web应用的开发更加方便、高效。同时,也是提高代码的可读性和维护性的关键所在。在实际开发过程中,根据具体需求选择最合适的参数接收方式,能够有效提升开发效率和应用性能。
83 2
|
2月前
|
前端开发 Java 应用服务中间件
我以为我对Spring MVC很了解,直到我遇到了...
所有人都知道Spring MVC是是开发的,却鲜有人知道Spring MVC的理论基础来自于1978 年提出MVC模式的一个老头子,他就是Trygve Mikkjel Heyerdahl Reenskaug,挪威计算机科学家,名誉教授。Trygve Reenskaug的MVC架构思想早期用于图形用户界面(GUI) 的软件设计,他对MVC是这样解释的。MVC 被认为是解决用户控制大型复杂数据集问题的通用解决方案。最困难的部分是为不同的架构组件想出好的名字。模型-视图-编辑器是第一个。
111 1
我以为我对Spring MVC很了解,直到我遇到了...