Spring Boot 項目統一結果,統一異常,統一日誌,寫的太好了。。

来源:https://www.cnblogs.com/javastack/archive/2022/06/15/16379284.html
-Advertisement-
Play Games

作者:永動的圖靈機 鏈接:https://juejin.cn/post/6844904033488994317 統一結果返回 目前的前後端開發大部分數據的傳輸格式都是json,因此定義一個統一規範的數據格式有利於前後端的交互與UI的展示。 統一結果的一般形式 是否響應成功; 響應狀態碼; 狀態碼描述 ...


作者:永動的圖靈機
鏈接:https://juejin.cn/post/6844904033488994317

統一結果返回

目前的前後端開發大部分數據的傳輸格式都是json,因此定義一個統一規範的數據格式有利於前後端的交互與UI的展示。

統一結果的一般形式

  1. 是否響應成功;
  2. 響應狀態碼;
  3. 狀態碼描述;
  4. 響應數據
  5. 其他標識符

結果類枚舉

  • 前三者可定義結果枚舉,如:success,code,message
@Getter
public enum ResultCodeEnum {
    SUCCESS(true,20000,"成功"),
    UNKNOWN_ERROR(false,20001,"未知錯誤"),,
    PARAM_ERROR(false,20002,"參數錯誤"),
    ;

    // 響應是否成功
    private Boolean success;
    // 響應狀態碼
    private Integer code;
    // 響應信息
    private String message;

    ResultCodeEnum(boolean success, Integer code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }
}

統一結果類

  • 第5個屬於自定義返回,利用前4者可定義統一返回對象

註意:

  1. 外接只可以調用統一返回類的方法,不可以直接創建,影刺構造器私有;
  2. 內置靜態方法,返回對象;
  3. 為便於自定義統一結果的信息,建議使用鏈式編程,將返回對象設類本身,即return this;
  4. 響應數據由於為json格式,可定義為JsonObject或Map形式;
@Data
public class R {
    private Boolean success;

    private Integer code;

    private String message;

    private Map<String, Object> data = new HashMap<>();

    // 構造器私有
    private R(){}

    // 通用返回成功
    public static R ok() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        r.setCode(ResultCodeEnum.SUCCESS.getCode());
        r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        return r;
    }

    // 通用返回失敗,未知錯誤
    public static R error() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.UNKNOWN_ERROR.getSuccess());
        r.setCode(ResultCodeEnum.UNKNOWN_ERROR.getCode());
        r.setMessage(ResultCodeEnum.UNKNOWN_ERROR.getMessage());
        return r;
    }

    // 設置結果,形參為結果枚舉
    public static R setResult(ResultCodeEnum result) {
        R r = new R();
        r.setSuccess(result.getSuccess());
        r.setCode(result.getCode());
        r.setMessage(result.getMessage());
        return r;
    }

    /**------------使用鏈式編程,返回類本身-----------**/
    
    // 自定義返回數據
    public R data(Map<String,Object> map) {
        this.setData(map);
        return this;
    }

    // 通用設置data
    public R data(String key,Object value) {
        this.data.put(key, value);
        return this;
    }

    // 自定義狀態信息
    public R message(String message) {
        this.setMessage(message);
        return this;
    }

    // 自定義狀態碼
    public R code(Integer code) {
        this.setCode(code);
        return this;
    }

    // 自定義返回結果
    public R success(Boolean success) {
        this.setSuccess(success);
        return this;
    }
}

控制層返回

  • 視圖層使用統一結果
@RestController
@RequestMapping("/api/v1/users")
public class TeacherAdminController {

    @Autowired
    private UserService userService;

    @GetMapping
    public R list() {
        List<Teacher> list = teacherService.list(null);
        return R.ok().data("itms", list).message("用戶列表");
    }
}    
  • json結果
{
  "success": true,
  "code": 20000,
  "message": "查詢用戶列表",
  "data": {
    "itms": [
      {
        "id": "1",
        "username": "admin",
        "role": "ADMIN",
        "deleted": false,
        "gmtCreate": "2019-12-26T15:32:29",
        "gmtModified": "2019-12-26T15:41:40"
      },{
        "id": "2",
        "username": "zhangsan",
        "role": "USER",
        "deleted": false,
        "gmtCreate": "2019-12-26T15:32:29",
        "gmtModified": "2019-12-26T15:41:40"
      }
    ]
  }
}

統一結果類的使用參考了mybatis-plus中R對象的設計


統一異常處理

使用統一返回結果時,還有一種情況,就是程式的保存是由於運行時異常導致的結果,有些異常我們可以無法提前預知,不能正常走到我們return的R對象返回。

因此,我們需要定義一個統一的全局異常來捕獲這些信息,並作為一種結果返回控制層

@ControllerAdvice

該註解為統一異常處理的核心

是一種作用於控制層的切麵通知(Advice),該註解能夠將通用的@ExceptionHandler、@InitBinder和@ModelAttributes方法收集到一個類型,並應用到所有控制器上

該類中的設計思路:

  1. 使用@ExceptionHandler註解捕獲指定或自定義的異常;
  2. 使用@ControllerAdvice集成@ExceptionHandler的方法到一個類中;
  3. 必須定義一個通用的異常捕獲方法,便於捕獲未定義的異常信息;
  4. 自定一個異常類,捕獲針對項目或業務的異常;
  5. 異常的對象信息補充到統一結果枚舉中;

自定義全局異常類

@Data
public class CMSException extends RuntimeException {
    private Integer code;

    public CMSException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public CMSException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }

    @Override
    public String toString() {
        return "CMSException{" + "code=" + code + ", message=" + this.getMessage() + '}';
    }
}

統一異常處理器

// ...
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

    /**-------- 通用異常處理方法 --------**/
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R error(Exception e) {
        e.printStackTrace();
        return R.error();	// 通用異常結果
    }

    /**-------- 指定異常處理方法 --------**/
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public R error(NullPointerException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.NULL_POINT);
    }

    @ExceptionHandler(HttpClientErrorException.class)
    @ResponseBody
    public R error(IndexOutOfBoundsException e) {
        e.printStackTrace();
        return R.setResult(ResultCodeEnum.HTTP_CLIENT_ERROR);
    }
    
    /**-------- 自定義定異常處理方法 --------**/
    @ExceptionHandler(CMSException.class)
    @ResponseBody
    public R error(CMSException e) {
        e.printStackTrace();
        return R.error().message(e.getMessage()).code(e.getCode());
    }
}

控制層展示

以下為展示當遇到null指定異常時,返回的結果信息

{
  "success": false,
  "code": 20007,
  "message": "空指針異常",
  "data": {}
}

統一日誌收集

日誌是追蹤錯誤定位問題的關鍵,尤其在生產環境中,需要及時修複熱部署,不會提供開發者debug的環境,此時日誌將會是最快解決問題的關鍵

日誌的框架比較豐富,由於spring boot對logback的集成,因此推薦使用logback在項目中使用。

Spring Boot 基礎就不介紹了,推薦下這個實戰教程:

https://github.com/javastacks/spring-boot-best-practice

Logback

配置

以下直接貼出配置信息,介紹信息科直接參考備註

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日誌級別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果設置為WARN,則低於WARN的信息都不會輸出 -->
<!-- scan:當此屬性設置為true時,配置文檔如果發生改變,將會被重新載入,預設值為true -->
<!-- scanPeriod:設置監測配置文檔是否有修改的時間間隔,如果沒有給出時間單位,預設單位是毫秒。
                 當scan為true時,此屬性生效。預設的時間間隔為1分鐘。 -->
<!-- debug:當此屬性設置為true時,將列印出logback內部日誌信息,實時查看logback運行狀態。預設值為false。 -->
<configuration  scan="true" scanPeriod="10 seconds">
    <contextName>logback</contextName>

    <!-- name的值是變數的名稱,value的值時變數定義的值。通過定義的值會被插入到logger上下文中。定義後,可以使“${}”來使用變數。 -->
    <property name="log.path" value="D:/Documents/logs/edu" />

    <!--0. 日誌格式和顏色渲染 -->
    <!-- 彩色日誌依賴的渲染類 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <!-- 彩色日誌格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH: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}}"/>

    <!--1. 輸出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日誌appender是為開發使用,只配置最底級別,控制台輸出的日誌級別是大於或等於此級別的日誌信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 設置字元集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--2. 輸出到文檔-->
    <!-- 2.1 level為 DEBUG 日誌,時間滾動輸出  -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在記錄的日誌文檔的路徑及文檔名 -->
        <file>${log.path}/edu_debug.log</file>
        <!--日誌文檔輸出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 設置字元集 -->
        </encoder>
        <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日誌歸檔 -->
            <fileNamePattern>${log.path}/web-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日誌文檔保留天數-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日誌文檔只記錄debug級別的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>debug</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.2 level為 INFO 日誌,時間滾動輸出  -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在記錄的日誌文檔的路徑及文檔名 -->
        <file>${log.path}/edu_info.log</file>
        <!--日誌文檔輸出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日誌歸檔路徑以及格式 -->
            <fileNamePattern>${log.path}/web-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日誌文檔保留天數-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日誌文檔只記錄info級別的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.3 level為 WARN 日誌,時間滾動輸出  -->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在記錄的日誌文檔的路徑及文檔名 -->
        <file>${log.path}/edu_warn.log</file>
        <!--日誌文檔輸出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此處設置字元集 -->
        </encoder>
        <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/web-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日誌文檔保留天數-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日誌文檔只記錄warn級別的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>warn</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 2.4 level為 ERROR 日誌,時間滾動輸出  -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在記錄的日誌文檔的路徑及文檔名 -->
        <file>${log.path}/edu_error.log</file>
        <!--日誌文檔輸出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此處設置字元集 -->
        </encoder>
        <!-- 日誌記錄器的滾動策略,按日期,按大小記錄 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/web-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日誌文檔保留天數-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日誌文檔只記錄ERROR級別的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--
        <logger>用來設置某一個包或者具體的某一個類的日誌列印級別、
        以及指定<appender>。<logger>僅有一個name屬性,
        一個可選的level和一個可選的addtivity屬性。
        name:用來指定受此logger約束的某一個包或者具體的某一個類。
        level:用來設置列印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
              還有一個特俗值INHERITED或者同義詞NULL,代表強制執行上級的級別。
              如果未設置此屬性,那麼當前logger將會繼承上級的級別。
        addtivity:是否向上級logger傳遞列印信息。預設是true。
        <logger name="org.springframework.web" level="info"/>
        <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
    -->

    <!--
        使用mybatis的時候,sql語句是debug下才會列印,而這裡我們只配置了info,所以想要查看sql語句的話,有以下兩種操作:
        第一種把<root level="info">改成<root level="DEBUG">這樣就會列印sql,不過這樣日誌那邊會出現很多其他消息
        第二種就是單獨給dao下目錄配置debug模式,代碼如下,這樣配置sql語句會列印,其他還是正常info級別:
        【logging.level.org.mybatis=debug logging.level.dao=debug】
     -->

    <!--
        root節點是必選節點,用來指定最基礎的日誌輸出級別,只有一個level屬性
        level:用來設置列印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
        不能設置為INHERITED或者同義詞NULL。預設是DEBUG
        可以包含零個或多個元素,標識這個appender將會添加到這個logger。
    -->

    <!-- 4. 最終的策略 -->
    <!-- 4.1 開發環境:列印控制台-->
    <springProfile name="dev">
        <logger name="com.cms" level="info"/>
        <root level="info">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="DEBUG_FILE" />
            <appender-ref ref="INFO_FILE" />
            <appender-ref ref="WARN_FILE" />
            <appender-ref ref="ERROR_FILE" />
        </root>
    </springProfile>


    <!-- 4.2 生產環境:輸出到文檔-->
    <springProfile name="pro">
        <logger name="com.cms" level="warn"/>
        <root level="info">
            <appender-ref ref="ERROR_FILE" />
            <appender-ref ref="WARN_FILE" />
        </root>
    </springProfile>

</configuration>

日誌收集異常信息

日誌信息往往伴隨著異常信息的輸出,因此,我們需要修改統一異常的處理器,將異常信息以流的方式寫到日誌文件中

  • 異常信息文件工具類
@Slf4j
public class ExceptionUtil {

    /**
     * 列印異常信息
     */
    public static String getMessage(Exception e) {
        String swStr = null;
        try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
            swStr = sw.toString();
        } catch (IOException ex) {
            ex.printStackTrace();
            log.error(ex.getMessage());
        }
        return swStr;
    }
}
  • 修改統一異常處理器,將異常方法中的直接列印改為日誌輸入並列印
// ...
import lombok.extern.slf4j.Slf4j;

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**-------- 通用異常處理方法 --------**/
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R error(Exception e) {
        // e.printStackTrace();
        log.error(ExceptionUtil.getMessage(e));
        return R.error();
    }
    
   // ...
}   

註意

  1. 日誌的環境即spring.profiles.acticve,跟隨項目啟動;
  2. 啟動後,即可到自定目錄查找到生成的日誌文件;
  3. 本地idea調試時,推薦Grep Console插件可實現控制台的自定義顏色輸出

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 遞歸查找文件 引言 或許是文件太多,想找某個文件又忘記放哪了;又或者是項目改造,需要將外部調用介面進行改造,項目太多,又無法排查。那麼怎麼快速找到自己想要的內容就是一件值得思考的事情了。 根據特定內容尋找文件位置 package com.lizi.globalexception.Utils; imp ...
  • 大家好,本文我將繼續來剖析SpringCloud中負載均衡組件Ribbon的源碼。本來我是打算接著OpenFeign動態代理生成文章直接講Feign是如何整合Ribbon的,但是文章寫了一半發現,如果不把Ribbon好好講清楚,那麼有些Ribbon的細節理解起來就很困難,所以我還是打算單獨寫一篇文章 ...
  • phpcurl函數類模擬Curl get post header refer攜帶Cookie模擬訪問來源Refer模擬UseaAgent ...
  • 一個工作5年的粉絲找到我,他說參加美團面試,遇到一個基礎題沒回答上來。 這個問題是:“資料庫連接池有什麼用?以及它有哪些關鍵參數”? 我說,這個問題都不知道,那你項目裡面的連接池配置怎麼設置的? 你們猜他怎麼回答。懂得懂得啊。 好的,關於這個問題,我們來看看普通人和高手的回答。 普通人: 資料庫連接 ...
  • Principle of token bucket 隨著互聯網的發展,在處理流量的方法也不僅僅為 first-come,first-served,而在共用網路中實現流量管理的基本機制就是排隊。而公平演算法則是實現在優先順序隊列中基於哪些策略來排隊的”公平隊列“。Token Bucket 則是為公平排隊提 ...
  • 1. 指數增長模型 設第今年的人口為 \(x_0\),年增長率為 \(r\),預測 \(k\) 年後的人口為 \(x_k\),則有 \[ x_k = x_0(1+r)^k \tag{1} \] 這個模型的前提是年增長率 \(r\) 在 \(k\) 年內保持不變. 利用 (1) 式可以根據人口估計年增 ...
  • 日常開發中,我們都會用到線程池,一般會用execute()和submit()方法提交任務。但是當你用過CompletableFuture之後,就會發現以前的線程池處理任務有多難用,功能有多簡陋,CompletableFuture又是多麼簡潔優雅。 要知道CompletableFuture已經隨著Ja ...
  • 能夠理解服務監控三要素 能夠理解常用的APM系統優勢差異 能夠基於IDEA集成Skywalking Agent 能基於生產環境使用Skywalking Agent 掌握Rocketbot 性能分析 鏈路追蹤 儀錶盤應用 Webhook 1 Skywalking概述 隨著互聯網架構的擴張,分散式系統變 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...