作者:永动的图灵机
juejin.im/post/5e073980f265da33f8653f2e
统一结果返回
目前的前后端开发大部分数据的传输格式都是json,因此定义一个统一规范的数据格式有利于前后端的交互与UI的展示。
统一结果的一般形式
是否响应成功;
响应状态码;
状态码描述;
响应数据
其他标识符
结果类枚举
前三者可定义结果枚举,如:success,code,message
publicenumResultCodeEnum{ SUCCESS(true,20000,"成功"), UNKNOWN_ERROR(false,20001,"未知错误"),, PARAM_ERROR(false,20002,"参数错误"), ; //响应是否成功 privateBooleansuccess; //响应状态码 privateIntegercode; //响应信息 privateStringmessage; ResultCodeEnum(booleansuccess,Integercode,Stringmessage){ this.success=success; this.code=code; this.message=message; } }@Getter
统一结果类
第5个属于自定义返回,利用前4者可定义统一返回对象
注意:
外界只可以调用统一返回类的方法,不可以直接创建,因此构造器私有;
内置静态方法,返回对象;
为便于自定义统一结果的信息,建议使用链式编程,将返回对象设类本身,即return this;
响应数据由于为json格式,可定义为JsonObject或Map形式;
publicclassR{ privateBooleansuccess; privateIntegercode; privateStringmessage; privateMapdata=newHashMap<>();//构造器私有privateR(){}//通用返回成功publicstaticRok(){ Rr=newR(); r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess()); r.setCode(ResultCodeEnum.SUCCESS.getCode()); r.setMessage(ResultCodeEnum.SUCCESS.getMessage());returnr; }//通用返回失败,未知错误publicstaticRerror(){ Rr=newR(); r.setSuccess(ResultCodeEnum.UNKNOWN_ERROR.getSuccess()); r.setCode(ResultCodeEnum.UNKNOWN_ERROR.getCode()); r.setMessage(ResultCodeEnum.UNKNOWN_ERROR.getMessage());returnr; }//设置结果,形参为结果枚举publicstaticRsetResult(ResultCodeEnumresult){ Rr=newR(); r.setSuccess(result.getSuccess()); r.setCode(result.getCode()); r.setMessage(result.getMessage());returnr; }/**------------使用链式编程,返回类本身-----------**///自定义返回数据publicRdata(Mapmap){this.setData(map);returnthis; }//通用设置datapublicRdata(Stringkey,Objectvalue){this.data.put(key,value);returnthis; }//自定义状态信息publicRmessage(Stringmessage){this.setMessage(message);returnthis; }//自定义状态码publicRcode(Integercode){this.setCode(code);returnthis; }//自定义返回结果publicRsuccess(Booleansuccess){this.setSuccess(success);returnthis; } }@Data
控制层返回
视图层使用统一结果
@RequestMapping("/api/v1/users") publicclassTeacherAdminController{ @Autowired privateUserServiceuserService; @GetMapping publicRlist(){ Listlist=teacherService.list(null);returnR.ok().data("itms",list).message("用户列表"); } }@RestController
json结果
"success":true, "code":20000, "message":"查询用户列表", "data":{ "itms":[ { "id":"1", "username":"admin", "role":"ADMIN", "deleted":false, "gmtCreate":"-12-26T15:32:29", "gmtModified":"-12-26T15:41:40" },{ "id":"2", "username":"zhangsan", "role":"USER", "deleted":false, "gmtCreate":"-12-26T15:32:29", "gmtModified":"-12-26T15:41:40" } ] } }{
统一结果类的使用参考了mybatis-plus中R对象的设计
统一异常处理
使用统一返回结果时,还有一种情况,就是程序的保存是由于运行时异常导致的结果,有些异常我们可以无法提前预知,不能正常走到我们return的R对象返回。
因此,我们需要定义一个统一的全局异常来捕获这些信息,并作为一种结果返回控制层
@ControllerAdvice
该注解为统一异常处理的核心
是一种作用于控制层的切面通知(Advice),该注解能够将通用的@ExceptionHandler、@InitBinder和@ModelAttributes方法收集到一个类型,并应用到所有控制器上
该类中的设计思路:
使用@ExceptionHandler注解捕获指定或自定义的异常;
使用@ControllerAdvice集成@ExceptionHandler的方法到一个类中;
必须定义一个通用的异常捕获方法,便于捕获未定义的异常信息;
自定一个异常类,捕获针对项目或业务的异常;
异常的对象信息补充到统一结果枚举中;
自定义全局异常类
publicclassCMSExceptionextendsRuntimeException{ privateIntegercode; publicCMSException(Integercode,Stringmessage){ super(message); this.code=code; } publicCMSException(ResultCodeEnumresultCodeEnum){ super(resultCodeEnum.getMessage()); this.code=resultCodeEnum.getCode(); } @Override publicStringtoString(){ return"CMSException{"+"code="+code+",message="+this.getMessage()+'}'; } }@Data
统一异常处理器
importorg.springframework.web.bind.annotation.ControllerAdvice; importorg.springframework.web.bind.annotation.ExceptionHandler; importorg.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice publicclassGlobalExceptionHandler{ /**--------通用异常处理方法--------**/ @ExceptionHandler(Exception.class) @ResponseBody publicRerror(Exceptione){ e.printStackTrace(); returnR.error();//通用异常结果 } /**--------指定异常处理方法--------**/ @ExceptionHandler(NullPointerException.class) @ResponseBody publicRerror(NullPointerExceptione){ e.printStackTrace(); returnR.setResult(ResultCodeEnum.NULL_POINT); } @ExceptionHandler(HttpClientErrorException.class) @ResponseBody publicRerror(IndexOutOfBoundsExceptione){ e.printStackTrace(); returnR.setResult(ResultCodeEnum.HTTP_CLIENT_ERROR); } /**--------自定义定异常处理方法--------**/ @ExceptionHandler(CMSException.class) @ResponseBody publicRerror(CMSExceptione){ e.printStackTrace(); returnR.error().message(e.getMessage()).code(e.getCode()); } }//...
控制层展示
以下为展示当遇到null指定异常时,返回的结果信息
"success":false, "code":20007, "message":"空指针异常", "data":{} }{
本节介绍统一异常较为简略,详细参考:
https://juejin.im/post/5cbc744a6fb9a0685a3f01a7
统一日志收集
日志是追踪错误定位问题的关键,尤其在生产环境中,需要及时修复热部署,不会提供开发者debug的环境,此时日志将会是最快解决问题的关键
日志的框架比较丰富,由于spring boot对logback的集成,因此推荐使用logback在项目中使用。
Logback
关于logback的配置和介绍,可以参考官网或推荐博客glmapper的logback博客,logback-spring.xml配置文件
/xu_san_duo/article/details/80364600
配置
以下直接贴出配置信息,介绍信息可以直接参考备注
<configurationscan="true"scanPeriod="10seconds"> <contextName>logbackcontextName> <propertyname="log.path"value="D:/Documents/logs/edu"/> <conversionRuleconversionWord="clr"converterClass="org.springframework.boot.logging.logback.ColorConverter"/> <conversionRuleconversionWord="wex"converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/> <conversionRuleconversionWord="wEx"converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/> <propertyname="CONSOLE_LOG_PATTERN"value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-ddHH:mm:ss.SSS}){faint}%clr(${LOG_LEVEL_PATTERN:-%5p})%clr(${PID:-}){magenta}%clr(---){faint}%clr([%15.15t]){faint}%clr(%-40.40logger{39}){cyan}%clr(:){faint}%m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <appendername="CONSOLE"class="ch.qos.logback.core.ConsoleAppender"> <filterclass="ch.qos.logback.classic.filter.ThresholdFilter"> <level>debuglevel> filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}Pattern> <charset>UTF-8charset> encoder> appender> <appendername="DEBUG_FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/edu_debug.logfile> <encoder> <pattern>%d{yyyy-MM-ddHH:mm:ss.SSS}[%thread]%-5level%logger{50}-%msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/web-debug-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filterclass="ch.qos.logback.classic.filter.LevelFilter"> <level>debuglevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appendername="INFO_FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/edu_info.logfile> <encoder> <pattern>%d{yyyy-MM-ddHH:mm:ss.SSS}[%thread]%-5level%logger{50}-%msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/web-info-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filterclass="ch.qos.logback.classic.filter.LevelFilter"> <level>infolevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appendername="WARN_FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/edu_warn.logfile> <encoder> <pattern>%d{yyyy-MM-ddHH:mm:ss.SSS}[%thread]%-5level%logger{50}-%msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/web-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filterclass="ch.qos.logback.classic.filter.LevelFilter"> <level>warnlevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <appendername="ERROR_FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/edu_error.logfile> <encoder> <pattern>%d{yyyy-MM-ddHH:mm:ss.SSS}[%thread]%-5level%logger{50}-%msg%npattern> <charset>UTF-8charset> encoder> <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/web-error-%d{yyyy-MM-dd}.%i.logfileNamePattern> <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MBmaxFileSize> timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15maxHistory> rollingPolicy> <filterclass="ch.qos.logback.classic.filter.LevelFilter"> <level>ERRORlevel> <onMatch>ACCEPTonMatch> <onMismatch>DENYonMismatch> filter> appender> <springProfilename="dev"> <loggername="com.cms"level="info"/> <rootlevel="info"> <appender-refref="CONSOLE"/> <appender-refref="DEBUG_FILE"/> <appender-refref="INFO_FILE"/> <appender-refref="WARN_FILE"/> <appender-refref="ERROR_FILE"/> root> springProfile> <springProfilename="pro"> <loggername="com.cms"level="warn"/> <rootlevel="info"> <appender-refref="ERROR_FILE"/> <appender-refref="WARN_FILE"/> root> springProfile> configuration><?xml version="1.0"encoding="UTF-8"?>
日志收集异常信息
日志信息往往伴随着异常信息的输出,因此,我们需要修改统一异常的处理器,将异常信息以流的方式写到日志文件中
异常信息文件工具类
publicclassExceptionUtil{ /** *打印异常信息 */ publicstaticStringgetMessage(Exceptione){ StringswStr=null; try(StringWritersw=newStringWriter();PrintWriterpw=newPrintWriter(sw)){ e.printStackTrace(pw); pw.flush(); sw.flush(); swStr=sw.toString(); }catch(IOExceptionex){ ex.printStackTrace(); log.error(ex.getMessage()); } returnswStr; } }@Slf4j
修改统一异常处理器,将异常方法中的直接打印改为日志输入并打印
importlombok.extern.slf4j.Slf4j; @ControllerAdvice @Slf4j publicclassGlobalExceptionHandler{ /**--------通用异常处理方法--------**/ @ExceptionHandler(Exception.class) @ResponseBody publicRerror(Exceptione){ //e.printStackTrace(); log.error(ExceptionUtil.getMessage(e)); returnR.error(); } //... }//...
注意
日志的环境即spring.profiles.acticve,跟随项目启动;
启动后,即可到自定目录查找到生成的日志文件;
本地idea调试时,推荐Grep Console插件可实现控制台的自定义颜色输出
详细过程,可参考源代码:
/chetwhy/cloud-flow
——End——
推荐阅读
推荐七个不错的Spring Boot+Vue开源项目IntelliJ IDEA天天用,却不知道这些技巧?面试题:谈谈你对分布式系统原理的理解推荐10款效率可以翻倍的IDEA插件详解Spring boot启动原理
看完本文有收获?点赞、分享是最大的支持
明天见(。・ω・。)ノ♡
如果觉得《delphi自定义统一基础设置_Java项目构建基础:统一结果 统一异常 统一日志...》对你有帮助,请点赞、收藏,并留下你的观点哦!