1 异常映射

1.1 目标

使用异常映射对项目的异常和错误提示进行统一管理。

1.2 思路

对于普通的页面请求,异常映射机制捕获到handler方法抛出的异常后会响应为一个错误页面,对于处理ajax请求的handler方法抛出的异常则响应一个json。

SpringMVC提供了基于xml和注解方式的异常映射机制,而且通过xml视图控制器<mvc:view-controller>进行的请求映射的异常只能通过xml的异常映射机制获取,而通过@requestMapping注解进行的请求映射的异常既能通过xml也能通过注解方式的异常映射机制获取。

1.3 实现

1.3.1 基于xml的异常映射机制

SimpleMappingExceptionResolver

classpath:spring-webmvc.xml

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- key指定异常的全类名 -->
<!-- 标签指定对应视图,会经过视图解析器添加前后缀 -->
<prop key="java.lang.Exception">system-error</prop>
</props>
</property>
</bean>
1.3.2 基于注解的异常映射机制

首先需要写一个判断请求类型的工具方法:

CrowdUtil.java

    public static boolean judgeRequestType(HttpServletRequest request) {
String acceptHeader = request.getHeader("Accept");
String xRequestHeader = request.getHeader("X-Requested-With"); return acceptHeader.contains("application/json") || "XMLHttpRequest".equals(xRequestHeader);
}

① 一个判断请求类型的空指针异常映射

@ControllerAdvice
public class CrowdExceptionResolve {
// ExceptionHandler将一个异常类型和一个方法关联起来
@ExceptionHandler(value = NullPointerException.class)
public ModelAndView resolveNullsoftException(
HttpServletRequest request,
NullPointerException nullPointerException,
HttpServletResponse response) throws IOException {
// 判断是否为ajax请求
if (CrowdUtil.judgeRequestType(request)) {
String message = nullPointerException.getMessage();
// 将异常信息写入json
ResultEntity<Object> resultEntity = ResultEntity.failed(message);
Gson gson = new Gson();
String json = gson.toJson(resultEntity);
// json写入响应体
response.getWriter().write(json);
}
// 普通请求返回携带异常信息的视图
ModelAndView modelAndView = new ModelAndView();
String viewName = "system-error";
modelAndView.addObject("exception", nullPointerException);
modelAndView.setViewName(viewName); return modelAndView;
}

@ControllerAdvice 声明类为一个异常处理类

@ExceptionHandler(value = 'Exception.class') 将异常处理方法和异常名绑定

② 优化

一方面需要书写大量的异常映射,重复性的书写太多另一方面前后端分离的页面不需要进行请求的判定更不需要返回modelandview了。

    public void commonExceptionResolve(Exception e,
ServletRequest request,
ServletResponse response) throws IOException {
// 获取异常信息并写入json
String message = e.getMessage();
Gson gson = new Gson();
String json = gson.toJson(R.failed(message));
// 将json写入请求体
response.getWriter().write(json);
}

编写其他异常映射的时候只需要调用公共异常映射方法即可。

1.3.3 ☆创建自定义异常

① 以登录为例,创建一个自定义异常LoginFailedException

public class LoginFailedException extends RuntimeException{

    public LoginFailedException() {
super();
} public LoginFailedException(String message) {
super(message);
} public LoginFailedException(String message, Throwable cause) {
super(message, cause);
} public LoginFailedException(Throwable cause) {
super(cause);
} protected LoginFailedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

继承自RuntimeException或者CompilerException这里都可以

② 在登录业务service中抛出自定义异常

    /**
* 验证用户账户及密码
*
* @param formLoginName, formPassWord
* @return
*/
@Override
public void userVerification(String formLoginName, String formPassWord) { // 对密码进行md5加密
formPassWord = CrowdUtil.md5(formPassWord); UserExample userExample = new UserExample();
userExample.createCriteria().andLoginNameEqualTo(formLoginName);
List<User> users = userMapper.selectByExample(userExample);
// 用户名不存在
if (users.size() <= 0) {
throw new LoginFailedException(CrowdConstant.MASSAGE_LOGIN_FAILED);
} String dbPassword = users.get(0).getPassWord();
// 密码不正确
if (!Objects.equals(formPassWord, dbPassword)) {
throw new LoginFailedException(CrowdConstant.MASSAGE_LOGIN_FAILED);
}
}

③ 在异常处理类中捕获controller处理请求产生的异常

@Slf4j
@ControllerAdvice
public class CrowdExceptionResolve { @ExceptionHandler(value = LoginFailedException.class)
public void LoginFailedResolve(LoginFailedException e,
ServletRequest request,
ServletResponse response) throws IOException {
commonExceptionResolve(e, request, response);
}

异常处理类的功能总结来说就是捕获控制器产生异常时的异常信息以及请求,然后进行一些处理响应这些请求

如此一来,我们在控制器层面就不需要考虑各种情况直接返回成功的结果就可以了,因为不成功的请求都被异常处理器响应了,所以一般情况下除了查询的service其他都可以写成void的返回类型了

    @RequestMapping(value = "/login", method = RequestMethod.POST)
public R<Object> loginHandle(@RequestBody String requestBody) {
JSONObject jsonObject = JSON.parseObject(requestBody);
String loginName = (String) jsonObject.get("loginName");
String passWord = (String) jsonObject.get("passWord");
// 验证用户名和密码
userService.userVerification(loginName, passWord);
// 返回token
Map<String, String> map = userService.getTokenByLoginName(loginName); return R.successWithData(map);
}

2 拦截器

拦截器能够实现对请求的判断拦截,如为了实现登录检查,可以拦截所有的请求然后放行登录和退出的请求。注意放行退出是因为如果用户登录期间登录过期的话,这时候如果不放行用户需要登录之后才能退出,这显然是反人类的。

2.1 拦截器的创建 HandlerInterceptor接口

jdk8.0之后java不需要实现接口的全部方法,之前是需要实现接口HandlerInterceptorAdapter才能使用单个方法。

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info(request.getRequestURI());
String token = request.getHeader("X-Token");
R<Object> resultEntity;
// 请求头中不含token
if(token == null) {
resultEntity = new R<>(JWTConstant.JWT_ERRORCODE_EXPIRE, null, null); Gson gson = new Gson();
String json = gson.toJson(resultEntity); response.getWriter().write(json); return false;
}
CheckResult checkResult = JWTUtil.validateJwt(token);
// token过期或者不合法
if(!checkResult.isSuccess()) {
int errorCode = checkResult.getErrorCode();
resultEntity = new R<>(errorCode, null, null); Gson gson = new Gson();
String json = gson.toJson(resultEntity); response.getWriter().write(json); return false;
}
return true;
}
}

2.2 拦截器的注册

在Servlet3.0中,Spring容器(这里则是AppConfig配置类)是通过实现WebMvcConfigurer接口来接管MVC的,可以在里面通过实现对应方法完成和web-mvc.xml相同的工作,如

  • 配置SpringMVC注解驱动(如@ControllerAdvice,@ResponseBody,@RequestBody),这个其实是通过使用注解@EnableWebMvc实现的(这个注解特别多的坑。。开启之后进行单元测试会报错)

  • 开启对静态资源的访问

  • 配置视图解析器

  • 等等

而在这里我们使用前后端分离的工作模式因此不需要其他的功能。

@Configuration
// 开启mvc注解驱动
@EnableWebMvc
@ComponentScan(value = "com.hikaru.crowd", includeFilters = {
@ComponentScan.Filter(classes = {RestController.class})
}, useDefaultFilters = false)
public class AppConfig implements WebMvcConfigurer {
/**
* 拦截器注册
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/test")
.excludePathPatterns("/user/logout")
.excludePathPatterns("/user/login"); }
}

注意:拦截器是SpringMVC自己的,只有使用了SPringMVC的工程才能使用拦截器,因此拦截器只会拦截对控制器的访问请求,如jsp、html、css、image、js是不会进行拦截的!这也是上面的请求路径只写controller的路径的原因。

2.3 区分过滤器和拦截器以及web容器

拦截器:如上面所说,是SpringMVC自己的,只有使用了SPringMVC的工程才能使用拦截器,因此拦截器只会拦截对控制器的访问请求,如jsp、html、css、image、js是不会进行拦截的

过滤器:是servlet规范的一部分,任何的javaweb都可以使用,在url-pattern配置之后对所有的资源都能进行拦截,其中/表示拦截除了jsp的资源,/*表示拦截所有的资源。

web三大器:web三大器指的是servlet、filter以及listener。

servlet不必多说,代表web应用的ServletContext在每一个web中包含一个,常用的即是DispatcherServlet前端拦截器。

filter上面也介绍过了,常用到的比如编码过滤器CharacterEncodingFilter。

listener比较少见,目前用到过的则是ContextLoaderListener,servelet2.0时代,配置web.xml的时候,为了实现构建spring容器(实际上也整合了mybatis)和web容器之间的子父类关系,使用了这个监听器来实现监听web容器的加载来加载spring容器,从而实现spring容器接管mvc。

web三大器全部是配置在web容器当中的,对应着项目的实现接口AbstractAnnotationConfigDispatcherServletInitializer的web容器配置类WebApplicationInitializer,也起到了Servlet 2.0时代web.xml的作用

public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
filter.setForceRequestEncoding(true);
filter.setForceResponseEncoding(true); return new Filter[]{filter};
} @Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
} @Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{AppConfig.class};
} //@Override
//public void onStartup(ServletContext servletContext) throws ServletException {
// this.registerCharacterEncodingFilter(servletContext);
// super.onStartup(servletContext);
//} @Override
protected String[] getServletMappings() {
return new String[]{"/"};
} //public void registerCharacterEncodingFilter(ServletContext servletContext) {
// FilterRegistration ceFilter = servletContext.addFilter("ceFilter", CharacterEncodingFilter.class);
// ceFilter.setInitParameter("encoding", "UTF-8");
// ceFilter.setInitParameter("forceRequestEncoding", "true");
// ceFilter.setInitParameter("forceResponseEncoding", "true");
//
// ceFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
//}
}

【SSM项目】尚筹网(三)基于Servlet3.0项目搭建:异常映射和拦截器机制的更多相关文章

  1. Servlet3.0学习总结(三)——基于Servlet3.0的文件上传

    在Servlet2.5中,我们要实现文件上传功能时,一般都需要借助第三方开源组件,例如Apache的commons-fileupload组件,在Servlet3.0中提供了对文件上传的原生支持,我们不 ...

  2. Servlet3.0学习总结——基于Servlet3.0的文件上传

    Servlet3.0学习总结(三)——基于Servlet3.0的文件上传 在Servlet2.5中,我们要实现文件上传功能时,一般都需要借助第三方开源组件,例如Apache的commons-fileu ...

  3. Servlet学习:(三)Servlet3.0 上传文件

    转: Servlet学习:(三)Servlet3.0 上传文件 2018年08月03日 11:57:58 iDark_CSDN 阅读数:362   一.注意事项 客户端(浏览器) 表单的提交方法必须是 ...

  4. 三:SpringBoot-配置系统全局异常映射处理

    三:SpringBoot-配置系统全局异常映射处理 1.异常分类 1.1 业务异常 1.2 系统异常 2.自定义异常处理 2.1 自定义业务异常类 2.2 自定义异常描述对象 2.3 统一异常处理格式 ...

  5. Servlet3.0之八:基于Servlet3.0的文件上传@MultipartConfig

    在Servlet2.5中,我们要实现文件上传功能时,一般都需要借助第三方开源组件,例如Apache的commons-fileupload组件,在Servlet3.0中提供了对文件上传的原生支持,我们不 ...

  6. 小白的首个maven web项目Step1软件安装三(8.0.15mysql及workbench安装)

    直接先开始下 MySQL 和 Workbench(mysql的可视化工具) ,注意下得是镜像版 .msi 后缀的 (mysql是纯控制面板的呈现方式,想要界面化操作可以装可视化工具,这里我装的是wor ...

  7. java-基于Servlet3.0的文件上传

    Servlet3.0学习总结(三)——基于Servlet3.0的文件上传 在Servlet3.0中使用request.getParts()获取上传文件

  8. 项目支持Servlet3.0的新特性

    一.Servlet3.0介绍 Servlet3.0是Java EE6规范的一部分,Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署描述 ...

  9. Eclipse中使用Maven创建Servlet3.0 Web 项目

    摘要 Apache Maven是一个优秀的项目构建和管理工具,许多开源项目都使用Maven进行构建.由于最近工作中要用到Maven,于是这里记录下在Eclipse中使用Maven插件创建一个基于Ser ...

  10. newbee-mall开源项目被慕课网拿去做课程,然后我毫不知情,这又是什么骚操作?

    万万没想到,这种事情会发生在我身上. 之前写过<开源囧事>系列而且已经写了四篇,四次开源囧事如下: <开源囧事(一)捅娄子了,写个bug被国家信息安全漏洞共享平台抓到了?> & ...

随机推荐

  1. 解决Pycharm不能识别selenium的部分提示代码

    这是解决前,pycharm没有提示相关的webelement的代码,例如:send_keys, click 之类的 把鼠标指针放在 selenium 这个单词上就能看到这一串路径 然后找到这个路径的文 ...

  2. C/C++ 数据结构链式队列的定义与实现

    #include <iostream> #include <Windows.h> using namespace std; typedef struct _QNode{ int ...

  3. DASCTF NOV X联合出题人-PWN

    太忙了,下午4点才开始做,,剩下的以后补上 签个到 逻辑很简单两个功能的堆,一个就是申请heap.还有一个是检验如果校验通过就会得到flag 申请模块 ​ 中间0x886是个很恶心的东西,需要我们绕过 ...

  4. LeetCode224 基本计算器

    idea:刚开始是打算分类讨论,建立了数字栈和字符栈,按照传入字符当时两个栈的基本情况分类,结果讨论完之后分类太麻烦,导致分析完了之后漏洞不少.我觉得这道题难点在于括号和负号的处理,一开始将导致计算机 ...

  5. POI给单元格添加超链接(xls,xlsx)

    package com.topcheer.html; import java.io.FileOutputStream; import java.io.IOException; import org.a ...

  6. arpspoof、driftnet工具使用

    一.arpspoof.driftnet工具安装: 在kali liux中: 安装命令:apt install dsniff      apt install driftnet 二.使用arpspoof ...

  7. 在MDK 5中打开MDK 4工程要注意的问题1

    我是生手,对于MDK的理解还很简单.以下内容是遇到的一种情况. 有一个MDK 4工程,要求顺序点亮4个LED灯,工程已经建好. 在MDK 5中打开,编译都没问题,要烧写时,提示"can no ...

  8. Java 面试手撕代码

    1. 判断括号有效性 public static boolean fun5(String str) { HashMap<Character, Character> hashMap = ne ...

  9. 图像主题颜色提取(Median cut)

    前言 之前想对图片素材进行分类管理,除了打标签,还有一样是通过主题色进行分类.于是开始寻找能提取主主题色的工具,最后找到了大名鼎鼎的 Leptonica 库,其中就有中位切割算法的实现.下面附上中位切 ...

  10. LeetCode 周赛 336,多少人直接 CV?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 今天早上是 LeetCode 第 336 场周赛,你参加了吗?这场周赛整体质量比较高,但 ...