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三大器指的是servletfilter以及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. nginx日志分析--可视化

    1. 安装依赖 yum install glib2 glib2-devel GeoIP-devel ncurses-devel zlib zlib-devel -y wget https://gith ...

  2. Linux系统环境下部署jar程序实现后台运行1

    [ nohup java -jar xxx.jar --spring.profiles.active=prod > 日志文件名 2>&1 & ]

  3. springboot jodconverter openoffice 实现 office 文件 在线预览

    这个已是好几个月前做的,好多细节已记得不那边清楚了.今天部署到环境还遇到不少问题.总结下. 1.office 文件实现在线预览的一般思路就是,将文件转pdf,在通过pdf.js 实现在线浏览.这其中转 ...

  4. Vue3 向window注入方法 TS警告 元素隐式具有 "any" 类型,因为索引表达式的类型不为 "number" 问题解决。

    window['funcName'] = function(){}; // 'funcName'会标红警告 (window as any).funcName = function(){}; // 正确 ...

  5. Halcon代码导出到.net FrameWork/WPF

    1. 应用背景 在工业项目中,往往需要使用机器视觉结合人机界面开发特定的,面向工艺的项目.机器视觉中,Halcon无疑是功能强大的,能快速应用到项目的视觉产品,而WPF则是解决人机界面的利器.因此了解 ...

  6. 使用阿里云镜像安装tensorflow

    pip --default-timeout=1000 install --index-url https://mirrors.aliyun.com/pypi/simple tensorflow pip ...

  7. PCB封装设计建议:

    1,通孔型元器件建议孔直径比元器件管脚直径大0.2-0.3mm左右,焊盘铜皮外沿一般是0.3-1mm(相当于直径应该加0.6-2mm)宽大元件可再大一点,对于设计单面板的,则最小铜皮外沿应大于1mm以 ...

  8. go 发布

    rm test-serv.bingo build -o test-serv.bin main.go

  9. MySQL学习(十一)为什么不推荐使用uuid和雪花id

    参考博客:https://www.cnblogs.com/wyq178/p/12548864.html 自增的主键的值是顺序的,所以Innodb把每一条记录都存储在一条记录的后面.当达到页面的最大填充 ...

  10. Spring Boot 入门学习笔记

    0x01 前言 ​ 大一选修课C++/JAVA二选一,选学了C++.但在后续课程中,发现JAVA的用途很多,所以简单学习了JAVA的语法.同时,也开始了我的Spring Boot 春季|家 (spri ...