SpringMVC拦截器和全局异常处理

SpringMVC拦截器和全局异常处理

1.拦截器定义与使用

什么是拦截器?

拦截器(Interceptor)是一种动态拦截Controller方法调用的对象,它可以在指定的方法调用前或者调用后,执行预先设定的代码。

拦截器作用类似于Filter(过滤器),但是它们的技术归属和拦截内容不同。Filter采用Servlet技术,拦截器采用Spring MVC技术;Filter会对所有的请求进行拦截,拦截器只针对Spring MVC的请求进行拦截。

拦截器的用途:

登录验证:在用户访问需要登录的页面之前,拦截器可以检查用户是否已经登录,如果没有登录则重定向到登录页面

权限检查:在用户访问需要特定权限的资源之前,拦截器可以检查用户是否具有相应的权限,如果没有则返回无权限的错误信息

日志记录:在前端访问后端每个接口时,拦截器可以记录日志,包括请求的 IP 地址、请求时间、请求的 URL 等信息

数据校验:在请求处理之前,拦截器可以校验请求参数的有效性,如果参数不合法则返回错误信息

统一异常处理:在请求处理出现异常时,拦截器可以统一处理异常信息,避免程序抛出异常页面

过滤器拦截器AOP编程
拦截对象URLURL方法
执行流程一次请求执行完成前后一次请求的相应处理器执行前后符合条件的方法调用前后
控制粒度过滤器 -> 拦截器 -> 切面,越来越细致
执行顺序过滤器 -> 拦截器 -> 切面
适用场景对服务器性能影响越小,如权限校验、敏感词过滤、字符集编码设置、响应数据压缩业务判断,如日志记录

拦截器定义方式

实现HandlerInterceptor接口定义拦截器

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        //在控制器方法调用前执行安全控制、权限校验等操作。
        //当返回值为true时,表示继续向下执行;当返回值为false时,整个请求就结束了,后续的所有操作都会中断(包括调用下一个拦截器和控制器类中的方法执行等)
        return false;
    }

    @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 {
        //在整个请求完成后执行,即视图渲染结束之后执行,完成一些资源清理、日志信息记录等工作。
    }
}


在springmvc.xml中注册自定义的拦截器MyInterceptor:

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!--拦截所有请求-->
    <bean class="edu.cqie.ssm.interceptor.MyInterceptor1"/>
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/**"/>
        <!-- 配置不需要拦截器作用的路径 -->
        <mvc:exclude-mapping path="/login"/>
        <!-- 对匹配路径的请求才进行拦截-->
        <bean class="edu.cqie.ssm.interceptor.MyInterceptor2" />
    </mvc:interceptor>
</mvc:interceptors>


Ø单个拦截器执行流程
在这里插入图片描述

Ø多个拦截器执行流程

在这里插入图片描述

2.全局异常处理

定义:异常程序运行过程中出现的一些错误,使用面向对象思想把这些错误来描述,那么一旦产生一个错误,即创建某一个错误对象,这个对象就是异常对象

类型:

在这里插入图片描述

自定义异常:

public class BizException extends RuntimeException {
    static final long serialVersionUID = 1234719074324978L;  
    public BizException(){} 
    public BizException(String message){ 
        super(message); 
    } 
    //测试自定义异常
    public static void main(String[] args) { 
        throw new BizException("自定义运行时异常");
    } 
}

Ø为什么全局异常处理?

编译时异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。在开发中,不管是Dao层、Service层还是Controller层,都有可能抛出异常。在SpringMVC中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。使用一个友好的页面显示,而不是一堆看不懂的错误信息。

Ø全局统一异常实现方式

•自带的SimpleMappingExceptionResolver

配置xml

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 定义默认的异常处理页面 -->
    <property name="defaultErrorView" value="error"/>
    <!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
    <property name="exceptionAttribute" value="ex"/>
    <!-- 定义需要特殊处理的异常,这是重要点 -->
    <property name="exceptionMappings">
        <props> 
            <!--异常类型 错误视图-->
            <prop key="java.lang.RuntimeException">error</prop>
            <!-- 还可以定义其他的自定义异常 -->
        </props>
    </property>
</bean>


•实现HandlerExceptionResolver接口

public class GlobalExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");//进入错误页面 
     if(ex instanceof RuntimeException){
            RuntimeException rtex = (RuntimeException)ex;
            mv.addObject("msg", rtex.getMessage());
        }
        return mv;
    }
}


同时注册bean

<bean class="edu.cqie.ssm.exception.GlobalExceptionResolver"/>

•@ControllerAdvice + @ExceptionHandler注解

@Component
@ControllerAdvice
@Log4j
@ResponseBody
public class GlobalExceptionHandler {
    /** 400 - Bad Request */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error(“参数解析失败”, e);
        return Result.fail(“Couldn‘t read request param.”);
    }
    /** 405 - Method Not Allowed */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error(“不支持当前请求方法”, e);
        return Result.fail(“Request method is not supported.”);
    }
    /** 415 - Unsupported Media Type */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result handleHttpMediaTypeNotSupportedException(Exception e) {
        log.error(“不支持当前媒体类型”, e);
        return Result.fail(“The current content type is not supported.”);
    }
    /** 500 - Internal Server Error */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        if (e instanceof BizException){
            log.error("业务逻辑出错啦"+e.getMessage());
            return Result.fail("The system has a business error.");
        }
        log.error("服务运行异常", e);
        e.printStackTrace();
        return Result.fail("Server error.");
    }
}


3.文件上传

(1)引入依赖

<!-- commons-fileupload -->
<dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.5</version>
</dependency>
<!-- commons-io -->
<dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.15.0</version>
</dependency>


(2)修改SpringMVC配置文件:在项目的src/main/resources目录下,新建spring-mvc.xml文件作为配置文件,并在该配置文件中创建id为multipartResolver的Bean。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="edu.cqie.ssm"/>
    <!--1.配置静态资源处理器-->
    <mvc:default-servlet-handler/>
    <!--2.支持mvc注解驱动-->
    <mvc:annotation-driven></mvc:annotation-driven>
    <!--3.视图解析器-->
    <mvc:view-resolvers>
        <mvc:jsp prefix="/WEB-INF/pages/" suffix=".jsp"/>
    </mvc:view-resolvers>
    <!--4.多部件解析器(Spring6以下版本)-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设定默认编码 -->
        <property name="defaultEncoding" value="UTF-8"></property>
        <!-- 设定文件上传的最大值为5MB,5*1024*1024 -->
        <property name="maxUploadSize" value="5242880"></property>
        <!-- 设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为10240 -->
        <property name="maxInMemorySize" value="40960"></property>
        <!-- 上传文件的临时路径 -->
        <property name="uploadTempDir" value="tmp/file"></property>
        <!-- 延迟文件解析 -->
        <property name="resolveLazily" value="true"/>
    </bean>
</beans>


(3)修改web.xml文件:从Spring6以后移除了org.springframework.web.multipart.commons.*包,要实现上传文件功能,只需在web.xml中节点内添加以下配置。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(4)添加控制器和视图:创建个人中心视图personal.jsp,提交修改后的用户信息。创建UserController控制器类,接收上传文件请求

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>个人中心</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/personal/modify" method="post" enctype="multipart/form-data">
    <input type="file" name="header" accept="image/*"/>
    <input type="text" name="name" />
    <input type="text" name="email" />
    <input type="submit" value="提交">
</form>
</body>
</html>


@RequestMapping("/personal/modify")
public ModelAndView modify(UserInfo userInfo, @RequestParam(value = "header", required = false)MultipartFile part, HttpServletRequest request){
    log.info("请求参数POJO包装类的值为:" +userInfo.toString());
    String name = "";
    if(!part.isEmpty()) {
        File dir = new File(request.getServletContext().getRealPath("/upload"));
        if (!dir.exists()) {
            dir.mkdirs();
        }
        name = String.valueOf(new Date().getTime() + "_" + part.getOriginalFilename());
        File destFile = new File(dir, name);
        try {
            part.transferTo(destFile);
        } catch (IllegalStateException | IOException e) {
            e.printStackTrace();
        }
    }
    // 访问的url
    String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/upload/" + name;
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("user", userInfo);
    modelAndView.addObject("img", url);
    modelAndView.setViewName("personal/detail");
    return modelAndView;
}


注意:

大多数文件上传都是通过表单形式提交给后台服务器,要实现文件上传功能,就需要提供一个文件上传的表单,并且必须满足以下3个条件:

•form表单的method属性设置为post。

•form表单的enctype属性设置为multipart/form-data。

•提供的文件上传选择框。

MultipartFile****常用方法

方法声明功能描述
byte[] getBytes()将文件转换为字节数组形式
String getContentType()获取文件的内容类型
InputStream getInputStream()读取文件内容,返回一个InputStream流
String getName()获取多部件form表单的参数名称
String getOriginalFilename()获取上传文件的初始化文件名
long getSize()获取上传文件的大小,单位是字节
boolean isEmpty()判断上传的文件是否为空
ng getContentType()获取文件的内容类型
InputStream getInputStream()读取文件内容,返回一个InputStream流
String getName()获取多部件form表单的参数名称
String getOriginalFilename()获取上传文件的初始化文件名
long getSize()获取上传文件的大小,单位是字节
boolean isEmpty()判断上传的文件是否为空
void transferTo(File file)将上传文件保存到目标目录下
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值