• 22-08-01 西安 尚医通(01)跨域配置、Swagger2、R类、统一异常处理和自定义异常、Logback日志


    一旦现实情况和脑海中预想的“应该”不符合,负面情绪就会奔涌而出,因为你的“世界秩序”崩塌了!


    跨域配置

    举例:在8081这个工程的前端只能去访问8081的后台,不能访问8082(不同的域)。这是浏览器的保护机制,但是我们可以通过某些设置,让前端页面可以访问不同的域。

    跨域:浏览器+ajax+请求不同的域。

    不同的域:协议+ip+端口,只要有一个不一样就是不同的域

    浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域 。前后端分离开发中,需要考虑ajax跨域的问题。

    解决办法:在Controller类上添加注解

    @CrossOrigin //跨域


    Swagger2

    Swagger2(丝袜):用来实时自动的生成一个api接口文档

    参数通常后端定,返回值前端定,Swagger2也可以当做postman使用

    Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

    1. 及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
    2. 规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
    3. 一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
    4. 可测性 (直接在接口文档上进行测试,以方便理解业务)
    当通过 Swagger 正确定义时,消费者可以使用最少量的实现逻辑来理解远程服务并与其进行交互。因此,Swagger 消除了调用服务时的猜测。

    swagger引入的主要依赖

    1. <dependency>
    2. <groupId>io.springfoxgroupId>
    3. <artifactId>springfox-swagger2artifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>io.springfoxgroupId>
    7. <artifactId>springfox-swagger-uiartifactId>
    8. dependency>

    创建swagger的配置类

    创建类Swagger2Config,配置文档的基本信息

    1. @Configuration
    2. @EnableSwagger2
    3. public class Swagger2Config {
    4. @Bean
    5. public Docket webApiConfig(){
    6. return new Docket(DocumentationType.SWAGGER_2)
    7. .groupName("webApi")
    8. .apiInfo(webApiInfo())
    9. .select()
    10. //只显示api路径下的页面
    11. //.paths(Predicates.and(PathSelectors.regex("/api/.*")))
    12. .build();
    13. }
    14. private ApiInfo webApiInfo(){
    15. return new ApiInfoBuilder()
    16. .title("网站-API文档")
    17. .description("微服务接口文档")
    18. .version("1.0")
    19. .contact(new Contact("atguigu", "http://atguigu.com", "111222333@qq.com"))
    20. .build();
    21. }
    22. }

    http://localhost:8201/swagger-ui.html


    Swagger2定义接口说明和参数说明

    使用sw注解标注在类上、方法上、参数上,返回值实体类上,用来进行功能的描述

    标注在Controller类上:@Api

    定义在方法上:@ApiOperation

    定义在参数上:@ApiParam

    用在实体类的属性上:@ApiModelProperty

    用在模型类上: @ApiModel

    1. //医院设置接口
    2. @Api(description = "医院设置接口")
    3. @RestController
    4. @RequestMapping("/admin/hosp/hospitalSet")
    5. public class HospitalSetController {
    6. @Autowired
    7. private HospitalSetService hospitalSetService;
    8. @ApiOperation(value = "根据id删除")
    9. @DeleteMapping("{id}")
    10. public R removeById(@ApiParam(name = "id", value = "医院设置主键", required = true) @PathVariable Long id) {
    11. boolean b = hospitalSetService.removeById(id);
    12. return R.ok();
    13. }
    14. }

    http://localhost:8201/swagger-ui.html#!/hospital45set45controller/removeByIdUsingDELETE

    可以再配置一个webApiConfig组,用来显示不同的controller


    R类

    作用:统一返回数据格式

    项目中一般我们会将所有接口的数据格式统一,并返回json给前端, 使前端对数据的操作更一致、轻松。

    一般会包括:数据状态、状态码、返回消息、数据

    在我们系统中状态码有俩种  20000成功   20001失败

    1. public class ResultCode {
    2. public static Integer SUCCESS = 20000;
    3. public static Integer ERROR = 20001;
    4. }

    创建的R类如下,可参考

    1. @Data
    2. @ApiModel(description = "通用的返回结果类")
    3. public class R {
    4. @ApiModelProperty(value = "是否成功")
    5. private Boolean success;
    6. @ApiModelProperty(value = "返回码")
    7. private Integer code;
    8. @ApiModelProperty(value = "返回消息")
    9. private String message;
    10. @ApiModelProperty(value = "返回数据")
    11. private Map data = new HashMap();
    12. private R(){}
    13. public static R ok(){
    14. R r = new R();
    15. r.setSuccess(true);
    16. r.setCode(ResultCode.SUCCESS);
    17. r.setMessage("成功");
    18. return r;
    19. }
    20. public static R error(){
    21. R r = new R();
    22. r.setSuccess(false);
    23. r.setCode(ResultCode.ERROR);
    24. r.setMessage("失败");
    25. return r;
    26. }
    27. public R success(Boolean success){
    28. this.setSuccess(success);
    29. return this;
    30. }
    31. public R message(String message){
    32. this.setMessage(message);
    33. return this;
    34. }
    35. public R code(Integer code){
    36. this.setCode(code);
    37. return this;
    38. }
    39. public R data(String key, Object value){
    40. this.data.put(key, value);
    41. return this;
    42. }
    43. public R data(Map map){
    44. this.setData(map);
    45. return this;
    46. }
    47. }

    测试 R类的效果:

    1.分页查询所有医院设置,代码很简单,如下使用MybatisPlus

    1. @ApiOperation(value = "分页查询")
    2. @GetMapping("{page}/{limit}")
    3. public R pageList(@PathVariable("page") Long page, @PathVariable("limit") Long limit) {
    4. //参数校验一般写在service层
    5. //page第几页 limit每页几条
    6. Page pageParam = new Page<>(page, limit);
    7. hospitalSetService.page(pageParam, null);
    8. //从分页对象获取分页数据
    9. List rows = pageParam.getRecords();
    10. //总记录数
    11. long total = pageParam.getTotal();
    12. return R.ok().data("total", total).data("rows", rows);
    13. }

    在Swagger上测试,查看R类有没有生效,效果如下图,是R类生效了的


    统一异常处理和自定义异常

    异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要

    统一异常处理器

    1.不使用统一的异常处理器之前,看看效果

    2.使用统一的异常处理器之后的返回值如下:

    全局的异常处理器定义如下:@Slf4j和log.error不是必须得,可以没有,这俩货就是为了记录日志的。

    1. @ControllerAdvice
    2. @Slf4j
    3. public class GlobalExceptionHandler {
    4. @ExceptionHandler(Exception.class)
    5. @ResponseBody
    6. public R error(Exception ex){
    7. //ex表示当前请求处理中出现的异常对象
    8. log.error(ex.getMessage());
    9. return R.error().message(ex.getMessage());
    10. }
    11. }

    自定义异常YyghException

    1. @Data
    2. @NoArgsConstructor
    3. @AllArgsConstructor
    4. public class YyghException extends RuntimeException {
    5. private String msg;
    6. private Integer code;
    7. }

     在GlobalExceptionHandler中加入以下特定的自定义的异常

    1. @ExceptionHandler(YyghException.class)
    2. @ResponseBody
    3. public R error(YyghException e){
    4. //e表示当前请求处理中出现的异常对象
    5. log.error(e.getMsg());//记录一条error级别的日志
    6. return R.error().message(e.getMsg()).code(e.getCode());
    7. }

    自定义异常的使用

    1. try {
    2. int a = 10/0;
    3. } catch (Exception e) {
    4. throw new YyghException("自定义异常", ResultCode.ERROR);
    5. }

    测试:此时出现自定义异常走的是下面这个,越精确越好


    Logback日志级别打印

    日志记录器(Logger)的行为是分等级的。

    分为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL

    OFF 不打印        ALL 都打印
    如果设置的是info级别,则info级别的前面所有级别的日志都打印
    默认情况下, spring boot从控制台打印出来的日志级别只有 INFO及以上级别,可以配置日志级别
    1. # 设置日志级别,这种方式只能将日志打印在控制台上
    2. logging.level.root=WARN

    Logback日志

    希望将日志记录在文件中,spring boot内部使用Logback作为日志实现的框架。

    1.删除application.properties中的日志配置 

    #logging.level.root=warn

    2.resources 中创建 logback-spring.xml

    每一个微服务都可以使用这个日志配置文件,但是要注意修改“log.path”的值


    1. <configuration scan="true" scanPeriod="10 seconds">
    2. <contextName>logbackcontextName>
    3. <property name="log.path" value="E:/yygh_log/service_hosp"/>
    4. <property name="CONSOLE_LOG_PATTERN"
    5. value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
    6. <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    7. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    8. <level>INFOlevel>
    9. filter>
    10. <encoder>
    11. <Pattern>${CONSOLE_LOG_PATTERN}Pattern>
    12. <charset>UTF-8charset>
    13. encoder>
    14. appender>
    15. <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    16. <file>${log.path}/log_info.logfile>
    17. <encoder>
    18. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    19. <charset>UTF-8charset>
    20. encoder>
    21. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    22. <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.logfileNamePattern>
    23. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    24. <maxFileSize>100MBmaxFileSize>
    25. timeBasedFileNamingAndTriggeringPolicy>
    26. <maxHistory>15maxHistory>
    27. rollingPolicy>
    28. <filter class="ch.qos.logback.classic.filter.LevelFilter">
    29. <level>INFOlevel>
    30. <onMatch>ACCEPTonMatch>
    31. <onMismatch>DENYonMismatch>
    32. filter>
    33. appender>
    34. <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    35. <file>${log.path}/log_warn.logfile>
    36. <encoder>
    37. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    38. <charset>UTF-8charset>
    39. encoder>
    40. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    41. <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern>
    42. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    43. <maxFileSize>100MBmaxFileSize>
    44. timeBasedFileNamingAndTriggeringPolicy>
    45. <maxHistory>15maxHistory>
    46. rollingPolicy>
    47. <filter class="ch.qos.logback.classic.filter.LevelFilter">
    48. <level>warnlevel>
    49. <onMatch>ACCEPTonMatch>
    50. <onMismatch>DENYonMismatch>
    51. filter>
    52. appender>
    53. <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    54. <file>${log.path}/log_error.logfile>
    55. <encoder>
    56. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    57. <charset>UTF-8charset>
    58. encoder>
    59. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    60. <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.logfileNamePattern>
    61. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
    62. <maxFileSize>100MBmaxFileSize>
    63. timeBasedFileNamingAndTriggeringPolicy>
    64. <maxHistory>15maxHistory>
    65. rollingPolicy>
    66. <filter class="ch.qos.logback.classic.filter.LevelFilter">
    67. <level>ERRORlevel>
    68. <onMatch>ACCEPTonMatch>
    69. <onMismatch>DENYonMismatch>
    70. filter>
    71. appender>
    72. <springProfile name="dev">
    73. <logger name="com.atguigu" level="INFO"/>
    74. <root level="INFO">
    75. <appender-ref ref="CONSOLE"/>
    76. <appender-ref ref="INFO_FILE"/>
    77. <appender-ref ref="WARN_FILE"/>
    78. <appender-ref ref="ERROR_FILE"/>
    79. root>
    80. springProfile>
    81. <springProfile name="pro">
    82. <root level="INFO">
    83. <appender-ref ref="CONSOLE"/>
    84. <appender-ref ref="DEBUG_FILE"/>
    85. <appender-ref ref="INFO_FILE"/>
    86. <appender-ref ref="ERROR_FILE"/>
    87. <appender-ref ref="WARN_FILE"/>
    88. root>
    89. springProfile>
    90. configuration>

    将错误日志输出到文件

    error级别只进了log_error.log文件,不进warn和info文件,其他俩个同理


    只需要俩处变化,即可实现把日志写入到文件中,如下图:

  • 相关阅读:
    MFC保存窗口客户区为图片
    4.14每日一题(二元函数求极值:常规方法、先代后求法)
    RBTree模拟实现
    Java版本+企业电子招投标系统源代码+支持二开+招投标系统+中小型企业采购供应商招投标平台
    超声功率放大器在MEMS超声测试中的应用
    3.DDD基本原理
    第一行代码Android 第九章9.4-9.5(解析JSON格式,网络编程最佳实践:发送HTTP请求的代码)
    技术社区项目—借助Logback 的扩展机制实现异常感知并进行邮件推送异常信息
    Node+Vue实现高校公寓管理系统设计与开发
    使用SPDK lib搭建自己的NVMe-oF Target应用
  • 原文地址:https://blog.csdn.net/m0_56799642/article/details/126103604