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. 字节过滤流 缓冲流-->BufferedInputStream用法

    1创建字节输入节点流FileInputStream fis = new FileInputStream("文件读取的路径");2创建字节输入过滤流,包装一个字节输入节点流Buffe ...

  2. 洛谷 P2330 [SCOI2005]繁忙的都市 题解

    START: 2021-08-05 15:30:20 题目链接: https://www.luogu.com.cn/problem/P2330 题目详情: 城市C是一个非常繁忙的大都市,城市中的道路十 ...

  3. hexo相对路径图片显示

    说明 hexo的图片默认不支持相对路径.需要配置 post_asset_folder 选项,设置从false改成true之后支持.但是要求图片目录必须和文件名相同. 由于我在typore下的markd ...

  4. ddddd

    项目二阶段总结 账户微服务 短信发送 1.压测发现问题 首先对短信smscomponent的send方法在test单元测试类中测试,不是真的发短信测试,可以建立请求开始和结束的时间戳来确定请求的耗时. ...

  5. STM32F103使用FSMC对接正点原子3.5寸TFTLCD屏幕

    fsmc的使用算是32里面有点绕的一个知识点,但是想明白了其实也没啥了. 首先我先放32个0在这儿: 0000  0000  0000  0000  0000  0000  0000  0000 [3 ...

  6. LoadRunner性能测试-app压力测试

    步骤分为三步: 一,录制脚本 录制脚本原理:启动LR代理服务器监听设置好的端口号是否有请求发送给服务器,有请求时,代理服务器接收请求,并转发给对应的系统服务器,LR从而获取到请求的信息与数据,生成脚本 ...

  7. .NET实验二

    实验名称:实验二 面向对象程序设计 一. 实验目的 1. 理解类的定义.继承等面向对象的的基本概念: 2. 掌握 C#语言定义类及其各种成员(字段,属性,方法)的方法: 3. 掌握方法覆盖的应用: 4 ...

  8. spring事件发布与监听

    一.组成部分 spring的事件监听有三个部分组成,事件(ApplicationEvent).监听器(ApplicationListener)和事件发布操作. 二.具体实现 事件 事件对象就是一个简单 ...

  9. tomcat的SSL配置

    Table of Contents 1. 删除别名为tomcat的密钥 2. 生成别名为tomcat的密钥 3. tomcat配置密钥存储路径 4. 生成证书并通过浏览器导入 5. 80,443端口重 ...

  10. 关于k8s微服务的基础知识分享总结

    1.说起k8s,先得讲讲微服务,来个图(百度上找到的图),初识 1.微服务架构强调的是一种架构模式,提倡将单一的应用程序,划分为一组小的服务,每个服务运行在其独立的自己的进程中,服务之间相互协调配合, ...