try catch对异常进行输出到日志、_spring Boot手把手教学(7): 抛弃try-catch, 如何优雅统一处理异常(含404)...
- 1、前言
- 2、自定义异常处理
- 3、完整代码
1、前言
我们在项目开发中,难免碰到业务代码异常,无论是server 500
, 还是其他异常。 我们这里简单说一下,如果抛弃try-catch
,统一进行异常响应处理;
❝ 错误类型:
1、自定义业务服务代码异常(根据各自项目需求)
2、ServletException HTTP请求异常
3、内部代码异常:比如 mysql 查询表名错误
4、请求接口404:这个在统一异常中无法获取,需要额外处理
❞
正常情况下,我们需要这么写:
❝ JobController
❞
// 查询所有@GetMapping("/list")public Result getList() {Result result;try {List<Job> jobList = jobService.findAll();result = ResultGenerator.getSuccessResult(jobList);} catch (Exception e) {e.printStackTrace();result = ResultGenerator.getFailResult(e.getMessage());}return result;}
结果如图:
这样比较麻烦,要写很多try-catch.
那么有没有一种办法,帮我们自动拦截错误呢?
2、自定义异常处理
HandlerExceptionResolver
, 就是处理异常的类;
❝ 源码:
❞
public interface HandlerExceptionResolver {@NullableModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}
以上有四个参数
其实相当于:request
(请求)、response
(响应)、hendler
(处理器)、exception
(异常);
接下来我们重写 WebMvcConfigurer.configureHandlerExceptionResolvers
来处理异常:
❝ com.scaffold.test.config.WebMvcConfig
❞
package com.scaffold.test.config;import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);// 统一异常处理@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {exceptionResolvers.add((request, response, handler, e) -> {Result result = new Result();// 错误处理if (e instanceof ServiceException) {// 1、业务失败的异常,如“账号或密码错误”result.setCode(ResultCode.FAIL).setMessage(e.getMessage());logger.info(e.getMessage());} else if (e instanceof ServletException) {// 2、调用失败result.setCode(ResultCode.FAIL).setMessage(e.getMessage());} else {// 3、内部错误result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");String message;if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",request.getRequestURI(),handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName(),e.getMessage());} else {message = e.getMessage();}result.setMessage(message);logger.error(message, e);}responseResult(response, result);return new ModelAndView();});}// 处理返回数据格式private void responseResult(HttpServletResponse response, Result result) {response.setCharacterEncoding("UTF-8");response.setHeader("Content-type", "application/json;charset=UTF-8");response.setStatus(200);try {response.getWriter().write(JSON.toJSONString(result));} catch (IOException ex) {logger.error(ex.getMessage());}}}
❝ 1、业务失败的异常: ServiceException 服务异常类
❞
package com.scaffold.test.base;/*** 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer*/
public class ServiceException extends RuntimeException {public ServiceException() {}public ServiceException(String message) {super(message);}public ServiceException(String message, Throwable cause) {super(message, cause);}
}// 部分代码
if (e instanceof ServiceException) {// 1、业务失败的异常,如“账号或密码错误”result.setCode(ResultCode.FAIL).setMessage(e.getMessage());logger.info(e.getMessage());
}
报错400代码;
❝ 2、调用失败: Servlet异常
❞
if (e instanceof ServletException) {// 3、调用失败result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
}
结果如图:
❝ 3、内部代码异常:比如 mysql 查询表名错误
❞
String message;
if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",request.getRequestURI(),handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName(),e.getMessage());
} else {message = e.getMessage();
}
result.setMessage(message);
logger.error(message, e);
结果如图:
❝ 4、请求接口404:这个在统一异常中无法获取,需要额外处理
❞
404进入不了统一异常处理,如下图:
所以,全局异常处理捕获不到404错误,需要专门写一个类用来处理404异常
❝ com.scaffold.test.base.NotFoundException
❞
package com.scaffold.test.base;import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class NotFoundException implements ErrorController {private static final String ERROR_PATH = "/error";@RequestMapping(ERROR_PATH)public Result error() {Result result = new Result();// 2、接口不存在result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在");return result;}@Overridepublic String getErrorPath() {return ERROR_PATH;}
}
3、完整代码
❝ com.scaffold.test.base.Result: 统一返回实体类
❞
package com.scaffold.test.base;import lombok.Data;/*** 统一API响应结果封装*/@Data
public class Result {private int code;private String message = "success";private Object data;public Result setCode(ResultCode resultCode){this.code = resultCode.code;return this;}public Result setMessage(String message){this.message = message;return this;}public Result setData(Object data){this.data = data;return this;}}
❝ com.scaffold.test.base.ResultCode: 统一返回响应码枚举,参考HTTP状态码
❞
package com.scaffold.test.base;/*** 响应码枚举,参考HTTP状态码的语义*/public enum ResultCode {SUCCESS(200),//成功FAIL(400),//失败UNAUTHORIZED(401),//未认证(签名错误)NOT_FOUND(404),//接口不存在INTERNAL_SERVER_ERROR(500);//服务器内部错误public int code;ResultCode(int code) {this.code = code;}
}
❝ 统一异常拦截:com.scaffold.test.config.WebMvcConfig
❞
package com.scaffold.test.config;import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);// 统一异常处理@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {exceptionResolvers.add((request, response, handler, e) -> {Result result = new Result();// 异常处理if (e instanceof ServiceException) {// 1、业务失败的异常,如“账号或密码错误”result.setCode(ResultCode.FAIL).setMessage(e.getMessage());logger.info(e.getMessage());}else if (e instanceof ServletException) {// 2、调用失败result.setCode(ResultCode.FAIL).setMessage(e.getMessage());} else {// 3、内部其他错误result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");String message;if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",request.getRequestURI(),handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName(),e.getMessage());} else {message = e.getMessage();}result.setMessage(message);logger.error(message, e);}responseResult(response, result);return new ModelAndView();});}// 处理响应数据格式private void responseResult(HttpServletResponse response, Result result) {response.setCharacterEncoding("UTF-8");response.setHeader("Content-type", "application/json;charset=UTF-8");response.setStatus(200);try {response.getWriter().write(JSON.toJSONString(result));} catch (IOException ex) {logger.error(ex.getMessage());}}
}
❝ 404拦截处理:com.scaffold.test.base.NotFoundException
❞
package com.scaffold.test.base;import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class NotFoundException implements ErrorController {private static final String ERROR_PATH = "/error";@RequestMapping(ERROR_PATH)public Result error() {Result result = new Result();// 4、接口不存在result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在");return result;}@Overridepublic String getErrorPath() {return ERROR_PATH;}
}
本文使用 mdnice 排版
try catch对异常进行输出到日志、_spring Boot手把手教学(7): 抛弃try-catch, 如何优雅统一处理异常(含404)...相关推荐
- java 如何去掉http debug日志_Spring Boot手把手教学(3):从零配置logback日志
前言 使用logback我们可以自定义控制台日志输出,可以实现对不同日志不同级别打印到不同的文件中,可以对日志进行归档保存,并定时删除: 对于一个应用来讲,一个好的日志系统,对于对问题的追溯有很大的帮 ...
- try catch对异常进行输出到日志、_java安全编码指南之:异常处理
点击上方的蓝字关注我吧 程序那些事 简介 异常是java程序员无法避免的一个话题,我们会有JVM自己的异常也有应用程序的异常,对于不同的异常,我们的处理原则是不是一样的呢? 一起来看看吧. 异常简介 ...
- springboot 控制台输出错误信息_Spring boot使用logback实现日志配置
前言 日志是我们系统必备的功能之一,可以帮助我们开发人员定位系统的异常.错误以及运行流程的重要的工具.今天老顾就来介绍一下Spring boot的默认的logback日志框架. 常用日志组件 java ...
- springboot日志配输出路径配置_Spring Boot 日志配置方法(超详细)
默认日志 Logback : 默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台.在运行应用程序和其他例子时,你应该已经看到很多INFO级别的日志了. 从上图 ...
- log4j 压缩日志_Spring Boot 日志各种使用姿势,是时候捋清楚了!
来自公众号:江南一点雨 1. Java 日志概览 1.1 总体概览 1.2 日志级别 1.3 综合对比 1.4 最佳实践 2. Spring Boot 日志实现 2.1 Spring Boot 日志配 ...
- main方法 如何去掉http debug日志_Spring Boot 常见错误及解决方法
找不到配置?配置不对?配置被覆盖? Spring Boot 配置加载过程解析: 1.Spring Boot 配置的加载有着约定俗成的步骤: 从 resources 目录下加载 application. ...
- springboot打印sql日志_Spring boot 工程,http打印日志太多
问题描述:在Idea里创建了一个spring boot工程,写了一个类,主方法访问http接口,http client打印的日志太多,设置日志级别也不管用. 代码截图: 控制台输出: 求助?????
- log4j记录不同的日志_Spring boot中使用log4j记录日志
之前在Spring Boot日志管理 一文中主要介绍了Spring Boot中默认日志工具(logback)的基本配置内容.对于很多习惯使用log4j的开发者,Spring Boot依然可以很好的支持 ...
- springboot开启debug日志_Spring Boot SLF4J日志实例(五十)
默认情况下,SLF4j日志记录包含在Spring Boot Web应用程序中,只需要启用它就可以了. 注意:查看此Spring Boot Logback XML模板以了解默认的日志记录模式和配置. S ...
最新文章
- 微型计算机硬件中访问速度最快的,习题一计算机基础知识.doc.doc
- 分享HTML5 canvas 的总结
- 如何查询SAP Cloud for Customer系统升级和维护时间
- php实现tcp连接的原理,PHP实现TCP实例
- 最大公约数python语言算法_使用Python求解最大公约数的实现方法
- 生物医学基础--右腿驱动
- 获取Button脚本挂载的事件名
- 通用 USB 重定向和客户端驱动器注意事项
- JavaScript刷新和跳转
- 如何压缩PDF文件的大小
- 哈希碰撞,改变世界的原力
- 使用VLAN隔离虚拟机流量
- spring和jump区别_JUMP与 SKIP 的区别
- 多家高校网站被挂马 用户应小心QQ盗号木马
- latex 上标 下标怎么打
- iphone图片编辑画笔_iPhone手机怎么编辑图片?还不知道的话真的要了解一波了~...
- 仿照Flexstroe3写的一个员工管理应用 (三)
- 写给IT自学者的入门指南
- dsolve 的 用法
- 舞蹈迁移:EverybodyDanceNow reproduced in pytorch