day10-SpringBoot的異常處理

来源:https://www.cnblogs.com/liyuelian/archive/2023/03/22/17245712.html
-Advertisement-
Play Games

SpringBoot異常處理 1.基本介紹 預設情況下,SpringBoot提供/error處理所有錯誤的映射,也就是說當出現錯誤時,SpringBoot底層會請求轉發到/error這個映射路徑所關聯的頁面或者控制器方法。(預設異常處理機制) 要驗證這個點,我們只需要設置一個攔截器,當每次請求時都在 ...


SpringBoot異常處理

1.基本介紹

預設情況下,SpringBoot提供/error處理所有錯誤的映射,也就是說當出現錯誤時,SpringBoot底層會請求轉發到/error這個映射路徑所關聯的頁面或者控制器方法。(預設異常處理機制)

要驗證這個點,我們只需要設置一個攔截器,當每次請求時都在preHandle()中列印請求URI。在瀏覽器訪問不存在的路徑映射時:

  • 瀏覽器:SpringBoot會響應一個"whitelabel"的錯誤視圖,並以HTML格式呈現

    image-20230322161603841
  • 伺服器:後臺輸出請求的URI為

    image-20230322162917888
  • 整個過程:當瀏覽器訪問不存在的路徑映射時,就產生了錯誤。這時 SpringBoot(底層由預設錯誤視圖解析器 DefaultErrorViewResolver 請求轉發/error 路徑關聯的頁面,該路徑若沒有被排除,也會經過攔截器處理)會立即去請求 /error 映射關聯的資源(預設為"whitelabel" 錯誤視圖),然後返回給瀏覽器,我們就看到了這個錯誤視圖。

2.預設錯誤視圖解析器

在SpringBoot發現瀏覽器請求的路徑不存在後,底層發生了一系列的操作:

(1)當瀏覽器訪問不存在的路徑映射時,就產生了錯誤

(2)底層由 DefaultErrorViewResolver (預設錯誤視圖解析器)先去遍歷項目中設置的所有靜態資源路徑,嘗試從這些靜態資源路徑中找到/error/404.html文件

image-20230322193146584
//第一次執行resolve()方法:
//這裡的viewName就是狀態碼
private ModelAndView resolve(String viewName, Map<String, Object> model) {
   String errorViewName = "error/" + viewName;//第一次:errorViewName=error/404
   TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
         this.applicationContext);
   if (provider != null) {
      return new ModelAndView(errorViewName, model);
   }
   return resolveResource(errorViewName, model);//進入resolveResource()方法
}

遍歷項目中設置的所有靜態資源路徑,嘗試從這些靜態資源路徑中找到/error/404.html文件

//第一次執行resolveResource()方法
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
   //resources.getStaticLocations()就是項目中的靜態資源路徑,根據你的設置而變化
   for (String location : this.resources.getStaticLocations()) {
      try {
         Resource resource = this.applicationContext.getResource(location);
         resource = resource.createRelative(viewName + ".html");
         if (resource.exists()) {
             //如果找到了,就返回這個視圖
            return new ModelAndView(new HtmlResourceView(resource), model);
         }
      }
      catch (Exception ex) {
      }
   }
   return null;
}

(3)如果找不到,退而求其次,再遍歷項目中設置的所有靜態資源路徑,嘗試從這些靜態資源路徑中找到/error/4xx.html文件

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
    //再一次調用resolve()方法
   ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
   if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
      modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
   }
   return modelAndView;
}
//第二次執行resolve()方法:
//這裡的viewName就是狀態碼
private ModelAndView resolve(String viewName, Map<String, Object> model) {
   String errorViewName = "error/" + viewName;//第二次:errorViewName=/error/4xx
   TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
         this.applicationContext);
   if (provider != null) {
      return new ModelAndView(errorViewName, model);
   }
   return resolveResource(errorViewName, model);//進入resolveResource()方法
}

遍歷項目中設置的所有靜態資源路徑,嘗試從這些靜態資源路徑中找到/error/4xx.html文件

//第二次執行resolveResource()方法
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
    //遍歷項目中設置的所有靜態資源路徑
   for (String location : this.resources.getStaticLocations()) {
      try {
         Resource resource = this.applicationContext.getResource(location);
         resource = resource.createRelative(viewName + ".html");
         if (resource.exists()) {
             //如果找到了就返回
            return new ModelAndView(new HtmlResourceView(resource), model);
         }
      }
      catch (Exception ex) {
      }
   }
   return null;
}

示例:

image-20230322171113119 image-20230322171215459

(4)以上兩輪都找不到匹配的視圖,就執行下麵的方法(位於AbstractErrorController.java):

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
      Map<String, Object> model) {
   for (ErrorViewResolver resolver : this.errorViewResolvers) {
      ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
      if (modelAndView != null) {
         return modelAndView;
      }
   }
   return null;
}

如果以上方法仍然返回null,然後去BasicErrorController.java里執行如下方法:

@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
   HttpStatus status = getStatus(request);
   Map<String, Object> model = Collections
         .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
   response.setStatus(status.value());
   ModelAndView modelAndView = resolveErrorView(request, response, status, model);
    //modelAndView為null,產生一個預設的新的視圖,並返回
   return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
image-20230322172239527

(5)此時瀏覽器接收到的視圖就是這個預設的視圖

image-20230322172410912

3.攔截器VS過濾器

  1. 使用範圍不同

    (1)過濾器實現的是javax.servlet.Filter介面,而這個介面是在Servlet規範中定義的,也就是說過濾器Filter的使用依賴於Tomcat等容器,Filter只能在web程式中使用

    (2)攔截器是一個Spring組件,由Spring容器管理,並不依賴Tomcat等容器,是可以單獨使用的。它不僅能應用在web程式中,也能用於Application等程式中。

  2. 兩者的觸發時機也不同

    (1)過濾器Filter在請求進入容器後,在進入Servlet之前進行預處理。請求結束則是在servlet處理完以後。

    (2)攔截器Interceptor是在請求進入Servlet之後(即DispatcherServlet,前端控制器),在進入Controller之前進行預處理的,Controller中渲染了對應的視圖之後請求結束。

    image-20230322173641992
  3. 過濾器不會處理請求轉發,攔截器會處理請求轉發(前提是攔截器沒有放行此請求)。原因是過濾器處理的是容器接收過來的外部請求,而請求轉發是伺服器內部,Servlet之間的處理。但攔截器是Spring的一個組件,依然會去處理請求轉發,除非請求轉發的路徑被攔截器放行了。

例子演示1--過濾器與攔截器的執行順序

我們分別在SpringBoot項目中配置一個過濾器和攔截器,測試它們的執行順序

(1)創建過濾器

package com.li.thymeleaf.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author 李
 * @version 1.0
 */
@Component
@Slf4j
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("MyFilter的init()被調用...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("MyFilter的doFilter()被調用...");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        log.info("MyFilter的destroy()被調用...");
    }
}

(2)創建攔截器

package com.li.thymeleaf.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 李
 * @version 1.0
 */
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("MyInterceptor的preHandle()被執行...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("MyInterceptor的postHandle()被執行...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("MyInterceptor的afterCompletion()被執行...");
    }
}

在配置類中註冊攔截器,註入到spring容器中

package com.li.thymeleaf.config;

import com.li.thymeleaf.interceptor.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author 李
 * @version 1.0
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addInterceptor註冊自定義攔截器
        //addPathPatterns指定攔截器規則(攔截所有請求/**)
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**");

    }
}

(3)瀏覽器請求某一個資源,後臺輸出如下:

image-20230322175749679

例子演示2--過濾器不會處理請求轉發,攔截器會處理請求轉發

我們在瀏覽器請求一個不存在的資源如http://localhost:8080/xxx,後臺輸出如下:

image-20230322181445614

這是因為SpringBoot底層處理了/error,進行了請求轉發(預設錯誤視圖解析器請求轉發到 /error 路徑關聯的頁面)。而過濾器不會處理請求轉發,因此可以看到途中只有攔截器被調用了兩次。過濾器只在外部請求資源/xxx的時候被調用了一次。

4.自定義異常頁面

4.1自定義異常頁面說明

Spring Boot Reference Documentation

如果要顯示給定狀態代碼的自定義 HTML 錯誤頁,可以將文件添加到目錄中。 錯誤頁面可以是靜態 HTML(即,添加到任何靜態資源目錄下),也可以是使用模板構建的。 文件名應為確切的狀態代碼或系列掩碼。/error

例如,要映射到靜態 HTML 文件,目錄結構如下所示:404

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用 Mustache 模板映射所有錯誤,目錄結構如下所示:5xx

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.mustache
             +- <other templates>
  1. 如果發生了404錯誤,優先匹配 404.html,如果沒有,則匹配 4xx.html,再沒有則使用預設方式顯示錯誤
  2. 500.html 和 5xx.html 是一樣的邏輯

4.2應用實例

需求:自定義404.html、500.html、4xx.html、5xx.html,當發生相應錯誤時,顯示自定義的頁面信息。

image-20230322184316442

(1)4xx.html,使用thymeleaf標簽取出狀態碼和錯誤信息,其他頁面同理

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>4xx</title>
</head>
<body bgcolor="#cedafe">
<div style="text-align: center">
    <br/><br/><hr/>
    <h1>4xx.html :)</h1>
    狀態碼:<h1 th:text="${status}"></h1>
    錯誤信息:<h1 th:text="${error}"></h1>
    <hr/>
</div>
</body>
</html>

(2)5xx.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>5xx</title>
</head>
<body bgcolor="#cedafe">
<div style="text-align: center">
    <br/><br/><hr/>
    <h1>5xx.html :(</h1>
    狀態碼:<h1 th:text="${status}"></h1>
    錯誤信息:<h1 th:text="${error}"></h1>
    <hr/>
</div>
</body>
</html>

(3)404.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>404</title>
</head>
<body bgcolor="#cedafe">
<div style="text-align: center">
    <br/><br/><hr/>
    <h1>404 Not Found~~</h1>
    狀態碼:<h1 th:text="${status}"></h1>
    錯誤信息:<h1 th:text="${error}"></h1>
    <hr/>
</div>
</body>
</html>

(4)500.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>500</title>
</head>
<body bgcolor="#cedafe">
<div style="text-align: center">
    <br/><br/><hr/>
    <h1>500 伺服器內部發生錯誤 :(</h1>
    狀態碼:<h1 th:text="${status}"></h1>
    錯誤信息:<h1 th:text="${error}"></h1>
    <hr/>
</div>
</body>
</html>

(5)模擬500和405錯誤

package com.li.thymeleaf.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

/**
 * @author 李
 * @version 1.0
 */
@Controller
public class MyErrorController {
    //模擬一個伺服器內部錯誤500
    @GetMapping("/abc")
    public String abc() {
        int i = 10 / 0;
        return "manage";
    }

    //如果get方式請求此路徑,會產生405的客戶端錯誤
    @PostMapping("/xyz")
    public String xyz() {
        return "manage";
    }
}

(6)瀏覽器訪問不存在的資源時,顯示的是404.html。因為發生錯誤時首先會在靜態資源目錄中按照:404.html-->4xx.html的順序尋找視圖。

image-20230322190651578

(7)伺服器內部錯誤-500錯誤:

image-20230322192005789

(8)如果出現出現的是4開頭的錯誤,就會返回4xx.html

image-20230322192030282

5.全局異常處理

5.1全局異常說明

在 Java 程式發生異常時,可以通過全局異常來捕獲處理異常。

在 SpringBoot 中通過 @ControllerAdvice(修飾的類即為全局異常處理器)加上@ExceptionHandler(修飾方法)處理全局異常,它的底層由 ExceptionHandlerExceptionResolver 類支撐。

  • 預設異常處理機制,是通過不同的狀態碼(status),確定要返回的頁面
  • 而全局異常的處理,則是根據 Java異常種類,來顯式指定返回的錯誤頁面
  • 全局異常處理優先順序 > 預設異常處理優先順序

5.2全局異常-應用實例

需求:演示全局異常使用,當發生類似 ArithmeticException、NullPointException 時,不使用預設異常機制(通過狀態碼)匹配的 xxx.html,而是通過全局異常機制顯式指定的錯誤頁面。

(1)創建全局異常處理器 GlobalExceptionHandler.java

package com.li.thymeleaf.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * @author 李
 * @version 1.0
 */
@ControllerAdvice //標識一個全局異常處理器/對象,標識的類將會被註入到spring容器中
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 編寫方法,處理指定異常(這裡要處理的異常由你指定)
     * @param e     表示發生異常後傳遞的異常對象
     * @param model 將異常信息放入model,傳遞給下一個頁面
     * @return
     */
    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String handleException(Exception e, Model model) {
        log.info("異常信息={}", e.getMessage());
        //model的數據會自動放入request域中
        model.addAttribute("msg", e.getMessage());
        return "/error/global";//指定轉發到global.html
    }
}

(2)global.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>全局異常</title>
</head>
<body bgcolor="#cedafe">
<div style="text-align: center">
    <br/><br/><hr/>
    <h1>發生了全局異常/錯誤 :(</h1>
    錯誤信息:<h1 th:text="${msg}"></h1>
    <hr/>
</div>
</body>
</html>

(3)模擬一個500錯誤

image-20230322201755011

(4)瀏覽器訪問,可以看到顯示的是global.html而不是500.html,因為全局異常處理的優先順序高於預設異常處理

image-20230322201845096

5.3拓展

全局異常的兩個註解是通過 ExceptionHandlerExceptionResolver 類來支撐的,該類有一個重要方法:doResolveHandlerMethodException()

image-20230322211516908

執行上述方法時,會獲取異常發生的方法以及發生的異常。併在返回的模型和視圖類也會帶有這兩個數據,我們可以根據這兩個信息進行日誌輸出,在異常發生時可以迅速定位處理異常:

image-20230322212318719

日誌輸出:

image-20230322212424110

6.自定義異常的處理

6.1自定義異常說明

  1. 如果SpringBoot提供的異常不能滿足開發需求,我們也可以自定義異常。

  2. 自定義異常處理(若採用預設的處理機制) = 自定義異常類 + @ResponseStatus

    @ResponseStatus 底層是 ResponseStatusExceptionResolve,底層調用 response.sendError(statusCode,resolvedReason);

  3. 自定義異常的處理方式:

    • 當拋出自定義異常時,仍然會根據狀態碼,去匹配使用 xxx.html 顯示(預設的異常處理機制)。
    • 或者將自定義異常類,放在你創建的全局異常處理器中進行處理(全局異常處理機制)

6.2自定義異常-應用實例

需求:自定義一個異常類 AccessException,當用戶訪問某個無權訪問的路徑時,拋出該異常,顯示自定義異常的狀態碼。

(1)自定義異常類:AccessException.java

package com.li.thymeleaf.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @author 李
 * @version 1.0
 * 自定義的異常類
 * (1)如果繼承Exception,屬於編譯異常
 * (2)如果繼承RuntimeException,屬於運行異常(一般來說都是繼承RuntimeException)
 * (3)@ResponseStatus(value = HttpStatus.FORBIDDEN)
 * 	    指定發生此異常時,通過http協議返回的的狀態碼(403-Forbidden)
 */
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class AccessException extends RuntimeException {

    public AccessException() {
    }

    //提供一個構造器,可以指定信息
    public AccessException(String message) {
        super(message);
    }
}

(2)在Controller中模擬發生異常

//模擬發生 AccessException
@GetMapping("/errTest")
public String test(String name) {
    if (!"tom".equals(name)) {
        throw new AccessException();
    }
    return "manage";//視圖地址
}

(3)4xx.html(略)

(4)瀏覽器訪問localhost:8080/errTest?name=jack,返回的頁面如下:

image-20230322215629460

因為返回的狀態碼為403,根據預設的異常處理機制,這裡會尋找4xx.html頁面返回給瀏覽器。如果是全局異常處理機制的話就會走全局異常處理的流程。

6.3註意事項和使用細節

如果把自定義異常類放在全局異常處理器,因為全局異常處理優先順序高,因此會走全局異常的處理機制。

比如在上一個例子中,添加如下代碼:

package com.li.thymeleaf.exception;

import ...

/**
 * @author 李
 * @version 1.0
 * 全局異常處理器
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler({AccessException.class})
    public String handleException(Exception e, Model model,HandlerMethod handlerMethod) {
        log.info("出現異常的方法={}", handlerMethod.getMethod());
        log.info("異常信息={}", e.getMessage());
        //model的數據會自動放入request域中
        model.addAttribute("msg", e.getMessage());
        return "/error/global";//指定轉發到global.html
    }
}

瀏覽器訪問localhost:8080/errTest?name=jack,返回的頁面如下:(global.html)

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

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前段時間接觸了Three.js後,試著用他載入了一些模型three.js初體驗簡陋的瞭解了一下three.js的相關使用,並且寫下了第一篇文章。但是過後還是對很多一知半解,作為不會建模且目前沒有接觸到相關業務需求的開發,難道沒有模型就什麼 ...
  • 事件匯流排Mitt使用非常簡單,本篇隨筆介紹在Vue3+TypeScript 前端項目中使用的一些場景和思路。我們在Vue 的項目中,經常會通過emits 觸發事件來通知組件或者頁面進行相應的處理,不過我們使用事件匯流排Mitt來操作一些事件的處理,也是非常方便的。 ...
  • Uniapp中的easycom是一種組件自動註冊機制,可以讓開發者更加方便地使用和管理組件。本文詳細的介紹了關於easycom使用方法。 ...
  • <template> <!-- 組件屬性: selection:表格是否可勾選 querys: [搜索配置 { label: "所屬網關",//標題 name: 'gatewayId',//v-model的參數名 value: '',//v-model的參數值 type: "select",//控制項 ...
  • 模板方法模式(Template Method Pattern):定義一個行為的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個行為的結構即可重定義該行為的某些特定步驟。 這些步驟被稱為“具體操作”(Concrete Operations),而整個行為的結構和順序則被稱為“模板方法”(T ...
  • 設計模式是個老生常談的話題, 不同的人對此有不同的看法 新手可能會覺得設計模式難以理解, 並且也只與編程語言有關, 或者只與前端後端有關, 適用範圍很窄 或者就乾脆覺得這玩意兒沒啥卵用, 寫了那麼一大坨只是讓代碼變得複雜難懂, 不如直接複製黏貼刪刪改改來得方便 老手可能會覺得設計模式就是yyds, ...
  • 1. 測試真實的應用程式 1.1. 應該以實際產品的使用方式進行測試 1.2. 所有的基準測試通常都包括一個預熱期,在這期間,JVM可以將代碼編譯到最佳狀態 1.3. 微基準測試(microbenchmark) 1.3.1. 通過測量一小部分代碼的性能來確定多種實現中哪個最好 1.3.2. 必須讀取 ...
  • 1.模塊(Module)和包(Package) 1.1 理解模塊(Module) 理解為是一個py文件 module是組織單位,它自己獨立構成一個命名空間,它本身是一個Python object 在Python object裡面,還可以有很多其他的Python object 實際應用中, modul ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...