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. uni-app 小程序在iOS系统无法长按复制问题

    最近在使用uni-app开发移动端应用,有客户反映微信小程序版本在使用是无法长按复制问题,在安卓系统上却是正常的. 检查了下代码,对text标签都设置了selectable属性,寻找万能的度娘还是没有 ...

  2. Qt回车键提交文本代码

    QTextEdit 文本框中 回车键提交文本,Ctrl+回车键实现换行的代码: void QTextEdit::keyPressEvent(QKeyEvent * e) { e->ignore( ...

  3. Error java 错误 不支持发行版本5 ( 完美解决版)

    问题 在Intellij idea中新建了一个Maven项目,运行时报错如下:Error : java 不支持发行版本5 解决方案 1. 原因 是因为ideal中默认配置中有几个地方的jdk版本与实际 ...

  4. 【搭建】【转】PPTP

    https://blog.51cto.com/10802692/2177227?SOURCE=DRA

  5. Code UTF-8 Console GB2312 Linux 中文乱码

    Linux 系统方法:  LD_LIBRARY_PATH=. ./userdemo | iconv -f GB2312 -t utf8 Shell 方法  Shell 编码 改成GB2312 // 编 ...

  6. This will upgrade your R installation.

    sudo add-apt-repository ppa:marutter/rrutter sudo apt update sudo apt full-upgrade

  7. 关于proTable设置列固定,始终没有固定的效果的原因

    使用proTable设置操作列固定 const columns: ProColumns<IssueItem>[] = [ { title: '操作', valueType: 'option ...

  8. 2.27总结——JDBC学习

    今天初步了解了Javaweb的JDBC,了解其基础语句,以及连接数据库的方式,但是自我感觉很抽象,实际上手仍有些困难,需要参考模板,增删改查目前进度在增和查,继续努力,争取本学期尽快跟上同学学习进度!

  9. java8-并行计算

    java8提供一个fork/join framework,fork/join框架是ExecutorService接口的一个实现,它可以帮助你充分利用你电脑中的多核处理器,它的设计理念是将一个任务分割成 ...

  10. ssh基于主机名访问

    登录一台服务器我们可以用ssh user@IP这种方式 还有一种快捷的方式,就是基于主机名访问,这需要先配置 /etc/hosts文件 假如我们又两台主机 192.168.75.131/165 分别为 ...