六、SpringMVC异常处理
6.1 单个控制器异常处理
在系统当中, Dao、Service、Controller层代码出现都可能抛出异常。如果哪里产生异常就在哪里处理,则会降低开发效率。所以一般情况下我们会让异常向上抛出,最终到达DispatcherServlet中,此时SpringMVC提供了异常处理器进行异常处理,这样可以提高开发效率。
1、处理异常的控制器
//异常处理方法,添加@ExceptionHandler注解表示该方法是处理异常的方法,属性为处理的异常类。 //Exception:异常对象。Model:数据模型 @ExceptionHandler(java.lang.NullPointerException.class) public String exceptionHandler(Exception exception,Model model){ //模型中添加异常对象 model.addAttribute("msg",exception); //跳转到异常页面 return "error"; }
2、前端异常页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>异常</title> </head> <body> 出错了!${requestScope.msg} </body> </html>
6.2 全局异常处理
在控制器中定义异常处理方法只能处理该控制器类的异常,要想处理所有控制器的异常,需要定义全局异常处理类。
package com.zj.controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; //全局异常处理器处理所有控制器的异常,需要添加@ControllerAdvice注解 @ControllerAdvice public class GlobalExceptionHandler { //异常处理方法,添加@ExceptionHandler注解表示该方法是处理异常的方法,属性为处理的异常类。 //Exception:异常对象。Model:数据模型 @ExceptionHandler(java.lang.NullPointerException.class) public String exceptionHandler(Exception exception, Model model){ //模型中添加异常对象 model.addAttribute("msg",exception); //跳转到异常页面 return "error"; } }
6.3 自定义异常
以上方式都是使用的SpringMVC自带的异常处理器进行异常处理,我们还可以自定义异常处理器处理异常:
package com.zj.controller; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // 自定义异常处理器实现HandlerExceptionResolver接口,并放入Spring容器中 @Component public class MyExceptionHandler implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView modelAndView = new ModelAndView(); if (e instanceof NullPointerException) { modelAndView.setViewName("error"); } else { modelAndView.setViewName("error2"); } modelAndView.addObject("msg", e); return modelAndView; } }
七、SpringMVC拦截器
7.1 拦截器简介
SpringMVC的拦截器(Interceptor)也是AOP思想的一种实现方式。它与Servlet的过滤器(Filter)功能类似,主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
拦截器和过滤器的区别
- 拦截器是SpringMVC组件,而过滤器是Servlet组件。
- 拦截器不依赖Web容器,过滤器依赖Web容器。
- 拦截器只能对控制器请求起作用(不拦截静态资源),而过滤器则可以对所有的请求起作用。
- 拦截器可以直接获取IOC容器中的对象,而过滤器就不太方便获取。
7.2 拦截器使用
1、创建控制器方法
@RequestMapping("/m1") public String m1(){ System.out.println("控制器方法"); return "result"; }
2、创建拦截器类,该类实现HandlerInterceptor接口,需要重写三个方法:
- preHandle:请求到达Controller前执行的方法,返回值为true通过拦截器,返回值为false被拦截器拦截。
- postHandle:跳转到JSP前执行的方法
- afterCompletion:跳转到JSP后执行的方法
package com.zj.controller; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { // 请求到达Controller前执行,此时可以向request域添加数据。 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("请求到达Controller之前"); // 如果return false则无法到达Controller return true; } // 跳转到JSP前执行,此时可以向Request域添加数据 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { request.setAttribute("name","张三"); System.out.println("请求跳转到JSP前"); } // 跳转到JSP后执行,此时已经不能向Request域添加数据 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("请求跳转到JSP后"); } }
3、在SpringMVC核心配置文件中配置拦截器
<!--拦截器--> <mvc:interceptors> <mvc:interceptor> <!--配置拦截器的作用对象--> <mvc:mapping path="/**"/> <!--配置拦截器对象--> <bean class="com.zj.controller.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
4、JSP页面
<html> <body> <h2>Hello, ${requestScope.name}</h2> </body> </html>
7.3 全局拦截器
全局拦截器可以拦截所有控制器处理的URL,作用等于/**,配置方式如下:
<!-- 配置拦截器 --> <mvc:interceptors> <!-- 全局拦截器 --> <bean class="com.zj.interceptor.MyInterceptor"> </bean> </mvc:interceptors>
7.4 拦截器链与执行顺序
如果一个URL能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置文件中配置的从上到下执行,接下来我们再配置一个拦截器:
<!--拦截器--> <mvc:interceptors> <!--拦截器1--> <mvc:interceptor> <!--配置拦截器的作用对象--> <mvc:mapping path="/**"/> <!--配置拦截器对象--> <bean class="com.zj.Interceptor.MyInterceptor"/> </mvc:interceptor> <!--拦截器2--> <mvc:interceptor> <!--配置拦截器的作用对象--> <mvc:mapping path="/**"/> <!--配置拦截器对象--> <bean class="com.zj.Interceptor.MyInterceptor2"/> </mvc:interceptor> </mvc:interceptors>
结论:
- preHandle()顺序执行,postHandle()、afterComletion()逆序执行。
- 只要有一个preHandle()拦截,后面的preHandle(),postHandle()都不会执行。
- 只要相应的preHandle()放行,afterComletion()就会执行。
7.5 拦截器过滤敏感词案例
在系统中,我们需要将所有响应中的一些敏感词替换为***
,此时可以使用拦截器达到要求:
1、写控制器方法
@RequestMapping("/m2") public String m2(Model model){ model.addAttribute("name","大笨蛋"); return "index"; }
2、编写敏感词拦截器
package com.zj.Interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.util.Map; import java.util.Set; // 敏感词拦截器 public class SensitiveWordInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws UnsupportedEncodingException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/plain;charset=UTF-8"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { // 敏感词列表 String[] sensitiveWords = {"坏人","暴力","笨蛋"}; // 获取model中所有数据 Map<String, Object> model = modelAndView.getModel(); Set<Map.Entry<String, Object>> entries = model.entrySet(); // 遍历model for (Map.Entry<String, Object> entry : entries) { String key = entry.getKey(); String value = entry.getValue().toString(); // 将model值和敏感词列表遍历比对 for (String sensitiveWord : sensitiveWords) { // 如果model值包含敏感词,则替换 if(value.contains(sensitiveWord)){ String newStr = value.replaceAll(sensitiveWord, "**"); model.put(key, newStr); } } } } }
3、配置拦截器
<!-- 配置拦截器--> <mvc:interceptors> <!-- 敏感词拦截器 --> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.zj.interceptor.SensitiveWordInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
4、访问
八、SpringMVC跨域请求
8.1 同源策略
同源策略是浏览器的一个安全功能。同源,指的是两个URL的协议,域名,端口相同。浏览器出于安全方面的考虑,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
哪些不受同源策略限制:
- 页面中的
<a>
跳转、表单提交不会受到同源策略限制的。 - 静态资源引入也不会受到同源策略限制。如嵌入到页面中的
<script src="">
,<img src="">
,<link href="">
等。
最容易收到同源策略影响的就是Ajax请求。
8.2 跨域请求
当请求URL的协议、域名、端口三者中任意一个与当前页面URL不同时即为跨域。浏览器执行JavaScript脚本时,会检查当前请求是否同源,如果不是同源资源,就不会被执行。
当前页面URL | 被请求页面URL | 是否跨域 | 原因 |
http://www.baidu.com/ | http://www.baidu.com/index.html | 否 | |
http://www.baidu.com/ | https://www.baidu.com/index.html | 跨域 | 协议不同 |
http://www.baidu.com/ | http://www.jd.com/ | 跨域 | 主域名不同 |
http://www.baidu.com/ | http://jd.jd.com/ | 跨域 | 子域名不同 |
http://www.baidu.com:8080/ | http://www.baidu.com:7001/ | 跨域 | 端口号不同 |
8.3 控制器接收跨域请求
SpringMVC提供了注解@CrossOrigin解决跨域问题。用法如下:
@RequestMapping("/m3") @ResponseBody // 如果请求从http://localhost:8080发出,则允许跨域访问 @CrossOrigin("http://localhost:8080") public String m3(){ System.out.println("测试跨域请求"); return "success"; }