六、SpringBoot拦截器
1、HandlerInterceptor拦截器的使用
-
编写一个类实现HandlerInterceptor接口
/** * 实现一个拦截器: * 1.实现HandlerInterceptor接口,重写方法 * 2.在config配置文件中配置该拦截器 */ public class LoginInterceptor implements HandlerInterceptor { /** * 在目标路径执行之前执行 * 可以获取到request和response对象 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); Object user = session.getAttribute("loginUser"); if (user != null){//验证是否登录逻辑,若不为user不为null则通过 return true; } //执行到此,说明没有登录,将没有登录的信息保存到request域中,转发到登录页面 request.setAttribute("msg","请登录"); request.getRequestDispatcher("/login").forward(request,response); return false; } /** * 在目标方法执行之后执行 * 可以获取到request、response和modelAndView对象 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /** * 在视图渲染之后执行 * 可以获取到request、response和exception对象 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
-
在config中配置
@Configuration//标注这是一个控制类 public class WebConfig implements WebMvcConfigurer { //跟web相关的配置类实现WebMvcConfigurer接口实现接口即可 /** * 添加一个拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**")// 配置 /** 会拦截所有的请求,包括静态资源 .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**");//设置哪些请求是不会拦截的,/**表示所有请求 } }
2、HandlerInterceptor拦截原理
- 根据当前请求,找到HandlerExcutionChain[可以处理请求的hanlder一级handler的所有 拦截器]
- 先来顺序执行所有拦截器的 preHandle() 方法
- 如果当前拦截器的preHandle()方法返回true,则执行下一个拦截器的preHandle()方法
- 如果当前拦截器的preHandle()方法返回false,直接倒序执行所有已经执行了的拦截器的aftercompletion()方法
- 如果任何一个拦截器返回false,直接跳出,不再执行目标方法
- 所有拦截器都返回true,执行目标方法
- 倒序执行所有拦截器的postHandle()方法
- 前面的步骤有任何异常都会直接倒序触发afterCompletion()方法
- 页面成功渲染以后,也会倒序触发afterCompletion()方法
3、拦截器的执行流程图
4、SpringBoot实现文件上传
-
form表单中的文件上传
<form th:action="@{/upload}" method="POST"> <div class="form-group"> <label for="exampleInputFile">单个文件</label> <input type="file" name="singleImg" id="exampleInputFile"> <!--type="file"没有multiple属性时表示只能上传单个文件--> <!--type="file"添加multiple属性时表示可以上传多个文件--> </div> <div class="form-group"> <label for="exampleInputFile">多个文件</label> <input type="file" name="photos" multiple> </div> </form>
-
后台中controller的编写
@PostMapping("/upload") public String upload( @RequestPart("singleImg") MultipartFile singleImg, @RequestPart("photos") MultipartFile[] photos) throws IOException { log.info("singleImg={},photos={}",singleImg.getSize(),photos.length); if(!singleImg.isEmpty()){ //保存到文件服务器,OSS服务器 String originalFilename = singleImg.getOriginalFilename(); headerImg.transferTo(new File("H:\\cache\\"+originalFilename)); } if(photos.length > 0){ for (MultipartFile photo : photos) { if(!photo.isEmpty()){ String originalFilename = photo.getOriginalFilename(); photo.transferTo(new File("H:\\cache\\"+originalFilename)); } } } return "index"; }
-
SpringBoot中文件上传时存在大小的限制,可以在application.properties配置文件中修改
spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=100MB
5、文件上传实现原理
- 自动配置好StandardServletMultipartResolver[文件上传解析器]
- 原理步骤:
- 请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart)返回(MultipartHttpServletRequest)文件上传请求
- 参数解析器来解析请求中的文件内容封装成MultipartFile
- 将request中文件信息封装为一个Map: MultiValueMap<String,MultipartFile> FileCopyUtils实现文件流的拷贝
6、异常处理
- 默认情况下,SpringBoot提供/error处理所有错误的映射
- 对于机器客户端(如:postman),它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息,对于浏览器客户端,响应一个 "Whitelabel"错误视图,以html格式城乡吸纳同的数据
- 要对其进行自定义,添加View解析为error
- error目录下的4XX,5XX页面会被自动解析(4xx.html,5xx.html都会被解析)
- 要完全替换默认行为,可以实现ErrorController,并注册该类型的Bean定义,或添加ErrorAttributes类型的组件,使用现有的机制替换其内容
7、异常处理自动配置原理
- ErrorMvcAutoConfiguration : 自动配置异常处理规则
- 容器中的组件: 类型: DefaultErroributes -> id: errorAttributes
- public class DefaultErrorAttributes implements ErrorAttributes HandlerExceptionResolver
- DefaultErrorAttributes : 定义错误页面中可以包含哪些数据
- 容器中的组件: 类型: BasicErrorController -> id: basicErrorController (json + 白页 适配响应)
- 处理默认 /error路径的请求 : 页面响应 new ModelAndView(“error”,model);
- 容器中有组件 View -> id是error; (响应默认错误页)
- 容器中放组件 BeanNameViewResolver(驶入解析器) : 按照返回的视图名称作为组件的id去容器中寻找View对象
- 容器中的组件 : 类型 : DefaultErrorViewResolver -> id : conventionErrorViewResolver
- 如果发生错误,会以Http的状态码作为视图页地址(viewName),找到真正的页面
- error/4xx、5xx.html
- 容器中的组件: 类型: DefaultErroributes -> id: errorAttributes
8、异常处理流程
-
执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束,并且使用dispatchException
-
进入视图解析流程(页面渲染)
processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException)
-
mv = processHandlerException : 处理handler发生的异常,处理完成后返回ModelAndView
- 遍历所有的handlerExceptionResolvers,看谁能处理当前异常[HandlerExceptionResolver(处理器异常解析器)]
- 系统默认的 异常解析器:
- DefaultErrorAttributes
- HandlerExceptionResolverComposite
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
- DefaultErrorAttributes先处理异常,把异常信息保存到request域中,并返回null
- 默认没有人处理任何异常,所有异常会被抛出
- 如果没有任何人能处理异常,最终底层就发发送/error请求,会被底层的BasicErrorController处理
- 解析错误视图: 遍历所有的ErrorViewResolver 看谁能解析
- 默认的DefaultErrorViewResolver,作用是把响应状态码作为错误页的地址,error/500.html
- 模板引擎最终响应这个页面 error/500.html
9、定制异常处理
-
方式一:自定义错误页:
- error/404.html(error/500.html) : 有精确的错误状态码就匹配精确,没有就找4XX.html页面,如果都没有则触发白页
-
方式二 : @ControllerAdvice + @ExceptionHandler 处理全局异常 : 底层是 : ExceptionHandlerExceptionResovler支持完成的
@ControllerAdvice public class AdviceHandlerException { @ExceptionHandler({ArithmeticException.class,NullPointerException.class})//可以处理那些异常 public String handlerException(Exception e){ /* 异常处理的逻辑 */ return "login";//视图地址 } }
-
方式三 : @ResponseStatus + 自定义异常 : 底层是 ResponseStatusExceptionResolver , 把responsestatus注解的信息底层调用 response.sendError(statusCode,resolvedReason) : 由tomcat发送 /error
-
自定义异常类 MyException.java
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "自定义的异常") public class MyException extends RuntimeException{//java中所有自定义的异常必须继承RuntimeException类 public MyException(){}//提供空参构造器 public MyException(String message){//调用父类的构造器 super(message); } }
-
可以用来处理手动抛出的异常
-
-
方式四 : Spring底层的异常,如 参数类型转换异常 : DefaultHandlerExceptionResolver 处理框架底层异常
- response.sendError(HttpServletResponse.SC_BAD_REQUEST,ex.getMessage())
-
方式五 : 自定义类实现 HandlerExceptionResolver 处理异常 : 可以作为默认的全部异常处理规则
-
方式六 : ErrorViewResolver : 实现自定义处理异常
- response.sendError error请求就会转给controller
- 你的异常没有任何人能处理 tomcat底层response.sendError error请求会转给 controller
- basicErrorController 要去的页面地址是 ErrorViewResolver