SpringBoot-全局处理异常,时间格式,跨域,拦截器,监听器
1.全局异常处理
使用ControllerAdvice与ExceptionHandler注解
/**
* 全局异常处理程序
*
* @author
* @date
*/
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public JsonResult handleException(Exception e) {
e.printStackTrace();
return JsonResult.error(e.getMessage());
}
/**
* 处理业务异常
*
* @param e
* @return {@link JsonResult}
*/
@ExceptionHandler(BusinessException.class)
public JsonResult handleBusinessException(BusinessException e) {
e.printStackTrace();
return JsonResult.error(e.getMessage());
}
/**
* handle 方法参数无效异常
*
* @param e e
* @return {@link JsonResult}
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public JsonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
e.printStackTrace();
return JsonResult.error(e.getBindingResult().getFieldError().getDefaultMessage());
}
}
2.全局时间格式处理
配合第一种@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")联合使用了,哪些字段需要特殊对待的,就可以单独使用这个@JsonFormat注解进行处理了。
- 如果是后台接收前端的时间数据,前端传递string类型的时间数据,后端要转换成的Date类型数据,可以使用@DateTimeFormat注解来接收参数
- 如果后端向前端传递数据,默认是返回时间戳,如果想要优雅的格式,可以在模型的Date字段或get方法上使用@JsonFormat注解,这个注解上可以指定时间格式和时区
/**
* @description: 日期时间全局格式化
* @auth: wujiangbo
* @date: 2022-03-09 17:38
*/
@JsonComponent
public class LocalDateTimeSerializerConfig {
@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
/**
* Date 类型全局时间格式化
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilder() {
return builder -> {
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");//获取时区
DateFormat df = new SimpleDateFormat(pattern);//设置格式化模板
df.setTimeZone(tz);
builder.failOnEmptyBeans(false)
.failOnUnknownProperties(false)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.dateFormat(df);
}; }
/**
* LocalDate 类型全局时间格式化
*/
@Bean
public LocalDateTimeSerializer localDateTimeDeserializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
}
}
3.跨域问题
@Configuration
/**
* 告诉浏览器,我允许哪些服务器访问,哪些请求方式访问,是否运行携带请求头
*/
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//2.允许的域,不要写*,否则cookie就无法使用了
//config.addAllowedOriginPattern("*");
config.addAllowedOriginPattern("http://127.0.0.1:8081");
config.addAllowedOriginPattern("http://localhost:8081");
config.addAllowedOriginPattern("http://127.0.0.1:80");
config.addAllowedOriginPattern("http://localhost:80");
config.addAllowedOriginPattern("http://127.0.0.1");
config.addAllowedOriginPattern("http://localhost");
//3.是否允许发送Cookie信息
config.setAllowCredentials(true);
//4.允许的请求方式
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
//5.允许的头信息
config.addAllowedHeader("*");
//6.添加映射路径,我们拦截一切请求
UrlBasedCorsConfigurationSource configSource = new
UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
//7.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
4. 配置拦截器
如何定义SpringMVC的拦截器
SpringMVC 的拦截器主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、判断登录等功能上
第1步,定义拦截器:可以实现 HandlerInterceptor 接口来自定义拦截器,接口定义了三个方法,preHandler方法是在请求到达处理器之前执行,postHandler方法是在请求经过处理器之后、解析试图之前执行,afterCompletion方法是在视图渲染之后、返回客户端之前执行
第2步,配置拦截器:在springmvc的配置文件xml中或配置类中,配置所有拦截路径,以及需要放行的路径
SpringMVC的执行原理
- DispatcherServlet:请求打过来由DispatcherServlet处理,它是 SpringMVC 中的前端控制器(中央控制器), 负责接收 Request 并将 Request 转发给对应的处理组件
- HandlerMapping:HandlerMapping 维护了 url 和 Controller(Handler)的 映 射关系 。 DispatcherServlet 接 收 请求, 然 后 从 HandlerMapping 查找处理请求的Controller(Handler),标注了@RequestMapping 的每个 method 都可以看成是一个 Handler,HandlerMapping 在请求到达之后, 它的作用便是找到请求相应的处理器 Handler 和 Interceptors。
- HandlerAdapter:SpringMVC通过HandlerAdapter对Handler进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。它的作用就是按照特定的规则去执行 Controller (Handler)
- Handler : Controller (Handler)负责处理请求,Controller 执行后并返回 ModelAndView 对象,其中包括了数据模型和逻辑视图,ModelAndView 是封装结果 视图的组件。Handler把结果返回给HandlerAdapter,HandlerAdapter把结果返回给DispatcherServlet前端控制器。
- ViewResolver:DispatcherServlet收到ModelAndView,调用视图解析器(ViewResolver)来解析HandlerAdapter传递的ModelAndView。Handler执行完成后返回的是逻辑视图,也就是视图名字,一个String ,还有一个Model就是数据模型,封装成ModelAndView。ViewResolver视图解析的作用就是根据视图名,把本地模板文件(比如:xx.jsp;xx.ftl)解析为View视图对象。View用来渲染视图,也就是负责把Handler返回的数据模型model,填充到模板(jsp;ftl)形成html格式的静态内容。
- 最后就是把生成的html通过response写给浏览器,浏览器进行html渲染展示。
1.创建拦截器
- HandlerInterceptor是接口,我们可以实现该接口来定义拦截器,
- HandlerInterceptorAdapter是抽象类,它实现了HandlerInterceptor接口的子接口AsyncHandlerInterceptor,我们可以继承该类来定义拦截器,它简化拦截器的实现,默认preHandler返回true
/**
* 登录拦截器
*
* @author
* @date
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate redisTemplate;
/**
* 前置拦截器
*
* @param request 请求
* @param response 响应
* @param handler 处理器
* @return boolean
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
Boolean tag = false;
if (token == null) {
token = request.getParameter("token");
tag = true;
}
Object tokenObj = redisTemplate.opsForValue().get(token);
//token为null,说明没有登录,跳转到登录页面
if (tokenObj == null) {
responseMessage(response);
if (tag) {
response.sendRedirect("http://127.0.0.1/pages/login/login.html");
}
return false;
}
return true;
}
/**
* 被拦截后的响应消息
*
* @param response 响应
* @throws IOException ioexception
*/
private static void responseMessage(HttpServletResponse response) throws IOException {
//告诉浏览器响应数据类型
response.setContentType("application/json;charset=UTF-8");
//设置响应数据
PrintWriter writer = response.getWriter();
writer.write("{\"code\": \"403\", \"success\": false, \"msg\": \"请先登录\"}");
}
}
2.添加拦截器,使用拦截器
/**
* 登录拦截器配置类
*
* @author
* @date 2023/12/18
*/
@Configuration
public class LoginInterceptorConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//启用登录拦截器
registry.addInterceptor(loginInterceptor)
//拦截所有请求
.addPathPatterns("/**")
//登录放行
.excludePathPatterns("/login/**");
}
}
5.集成Servllet组件,配置监听器
第一步:继承ServletContextListener
public interface ServletContextListener extends EventListener {
//当servlet初始化上下文容器时触发
public default void contextInitialized(ServletContextEvent sce) {
// 获取Servlet上下文
ServletContext servletContext = sce.getServletContext();
// 获取springMVC上下文
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
}
//当servlet销毁上下文容器时触发
public default void contextDestroyed(ServletContextEvent sce) {
}
}
第二步:扫描监听器
@ServletComponentScan("cn.lgc.blog.listenter")//扫描servlet监听器
public class BlogApp {
public static void main(String[] args) {
SpringApplication.run(BlogApp.class, args);
}
}