spring mvc詳細講解(前後端分離模式)

来源:https://www.cnblogs.com/xxctx/p/18416245
-Advertisement-
Play Games

在前後端分離模式下,Spring MVC 的作用主要集中在處理後端的業務邏輯和 API 介面,而不再直接管理視圖部分。也就是說,Spring MVC 的重點是如何處理客戶端的請求並返回數據(通常以 JSON 或 XML 格式),而視圖渲染交給前端框架(如 Vue.js、React 等)來完成。 下麵 ...


在前後端分離模式下,Spring MVC 的作用主要集中在處理後端的業務邏輯和 API 介面,而不再直接管理視圖部分。也就是說,Spring MVC 的重點是如何處理客戶端的請求並返回數據(通常以 JSON 或 XML 格式),而視圖渲染交給前端框架(如 Vue.js、React 等)來完成。

下麵是針對前後端分離模式的 Spring MVC 詳細講解。

1、Spring MVC 工作流程

在前後端分離的模式下,Spring MVC 的工作流程仍然包含以下步驟,但其核心作用是處理客戶端發送的請求並返回數據,而不再渲染視圖。

  1. 客戶端發送請求:前端應用(如 Vue.js 或 React)通過 AJAX 或 Axios 向後端發送 HTTP 請求,通常是 RESTful API 請求。
  2. DispatcherServlet 攔截請求DispatcherServlet 作為 Spring MVC 的前端控制器,攔截所有的 HTTP 請求,並將請求轉發給合適的處理器(Controller)。
  3. 處理請求:Controller 根據請求路徑和請求參數,調用業務邏輯處理或資料庫操作,生成相應的響應數據。
  4. 返回 JSON 或 XML 數據:Controller 將處理結果作為 JSON 或 XML 格式的數據返回給客戶端。前端應用再根據返回的數據進行頁面更新或其他操作。

2、核心組件

在前後端分離的模式中,Spring MVC 的核心組件有以下幾個:

2.1 Controller(控制器)

控制器主要負責接收前端的請求,並返回數據。通常,控制器中的方法會通過 @RestController 註解來簡化開發,它表示這個控制器只會返回數據而不是視圖。

示例:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // 模擬從資料庫獲取用戶
        User user = new User(id, "John", 25);
        return user;  // 返回 JSON 數據
    }

    @PostMapping("/create")
    public User createUser(@RequestBody User user) {
        // 保存用戶邏輯
        return user;  // 返回保存後的用戶信息
    }
}

在這個例子中,UserController 通過 RESTful API 向前端返回用戶數據,而不是返回 HTML 頁面。

2.2 @RestController 註解

@RestController@Controller@ResponseBody 的組合註解,簡化了返回 JSON、XML 等格式的開發流程。每個方法返回的數據會自動序列化為客戶端期望的格式。

2.3 RequestMapping 註解

@RequestMapping 或簡化的 @GetMapping@PostMapping 用於定義介面的 URL 路徑,並指定 HTTP 方法類型(GET、POST、PUT、DELETE 等)。它們用來路由前端請求到具體的控制器方法。

2.4 @RequestBody 和 @ResponseBody

  • @RequestBody:用於從請求體中提取 JSON 或 XML 格式的數據並映射到方法參數上。前端通常發送 JSON 格式的數據,Spring MVC 會自動將其轉換為 Java 對象。
  • @ResponseBody:用於將方法的返回值自動序列化為 JSON 或 XML 格式返回給客戶端。方法的返回值會直接作為 HTTP 響應體返回,而不是視圖名稱。

3、RequestMapping

@RequestMapping 是 Spring MVC 中的核心註解之一,用於將 HTTP 請求映射到處理器方法(通常是控制器類中的方法)。在前後端分離模式下,@RequestMapping 和相關的註解主要用於構建 RESTful API,確保前端能通過 HTTP 請求與後端進行交互。@RequestMapping 可以精確地映射 HTTP 請求的 URL、請求方法、請求參數等,以便後端能夠根據前端的請求路徑和請求類型執行相應的操作。

1. @RequestMapping 註解的基本使用

@RequestMapping 可以用於類和方法上,常見的組合是將它用在控制器類上以指定公共路徑,方法上用來指定更具體的路徑和請求操作。

類級別的 @RequestMapping

類級別的 @RequestMapping 用於定義公共的 URL 路徑首碼,所有在此類中的方法共用這個首碼。

示例:

@RestController
@RequestMapping("/api/users")
public class UserController {
    // 所有路徑都會以 "/api/users" 為首碼
}

方法級別的 @RequestMapping

方法級別的 @RequestMapping 用於指定具體的請求路徑和操作,可以與類級別的路徑組合形成完整的 URL。

示例:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @RequestMapping("/list")
    public List<User> getAllUsers() {
        // 處理 "/api/users/list" 請求
        return userService.getAllUsers();
    }

    @RequestMapping("/details/{id}")
    public User getUserById(@PathVariable Long id) {
        // 處理 "/api/users/details/{id}" 請求
        return userService.getUserById(id);
    }
}

2. @RequestMapping 的常用屬性

@RequestMapping 提供了多個屬性用於更加精確地匹配 HTTP 請求:

2.1 value 屬性

value 屬性指定請求的路徑,可以是一個字元串數組,表示允許多個 URL 訪問同一個處理器方法。

@RequestMapping(value = {"/list", "/all"})
public List<User> getAllUsers() {
    // "/list" 或 "/all" 都會被映射到這個方法
    return userService.getAllUsers();
}

2.2 method 屬性

method 屬性用於指定支持的 HTTP 請求方法,例如 GETPOSTPUTDELETE 等。

@RequestMapping(value = "/create", method = RequestMethod.POST)
public User createUser(@RequestBody User user) {
    // 只處理 POST 請求
    return userService.saveUser(user);
}

如果你希望更簡潔地定義請求方法,可以使用特定的註解替代 @RequestMapping,如 @GetMapping@PostMapping@PutMapping@DeleteMapping

2.3 params 屬性

params 屬性可以用於限制請求參數的存在與否。例如,如果你希望方法僅在某個特定參數存在時被調用,可以使用 params 屬性。

http://example.com/login?username=yourUsername&password=yourPassword
@RequestMapping(value = "/search", params = {"username", "password"})
public List<User> searchUsersByName(@RequestParam String username, @RequestParam String password) {
    // 只有請求中包含 username 參數時,這個方法才會被執行
    return userService.searchByName(username);
}

2.4 headers 屬性

headers 屬性用於根據 HTTP 請求頭匹配請求。這個屬性可以用來在某些特定的請求頭存在時執行某個處理方法。

@RequestMapping(value = "/info", headers = "Accept=application/json")
public User getUserInfo() {
    // 僅在 Accept 頭為 "application/json" 時處理請求
    return userService.getUserInfo();
}

2.5 consumes 屬性

consumes 屬性用於指定請求的 Content-Type 類型。例如,如果你希望處理的請求體是 JSON 格式,可以這樣指定:

@RequestMapping(value = "/create", method = RequestMethod.POST, consumes = "application/json")
public User createUser(@RequestBody User user) {
    // 只接受 Content-Type 為 "application/json" 的請求
    return userService.saveUser(user);
}

2.6 produces 屬性

produces 屬性用於指定返回的響應類型(Content-Type)。例如,如果你希望返回 JSON 格式的響應,可以這樣定義:

@RequestMapping(value = "/user/{id}", produces = "application/json")
public User getUserById(@PathVariable Long id) {
    // 返回 JSON 格式的數據
    return userService.getUserById(id);
}

2.7 path 屬性

pathvalue 的別名,用於定義請求的 URL 路徑。如果需要更具語義化地定義路徑,可以使用 path 屬性。

@RequestMapping(path = "/details/{id}")
public User getUserDetails(@PathVariable Long id) {
    return userService.getUserDetails(id);
}

3. 組合使用

@RequestMapping 可以組合使用多個屬性來實現更加複雜的請求匹配。例如:

@RequestMapping(
    value = "/update",
    method = RequestMethod.PUT,
    consumes = "application/json",
    produces = "application/json"
)
public User updateUser(@RequestBody User user) { // @RequestBody 註解用於將請求體中的 JSON 數據轉換為 User 對象。
    // 只處理 PUT 請求,且請求體和響應體都是 JSON 格式
    return userService.updateUser(user);
}

4. 常見註解簡化形式

為了簡化常見的 HTTP 操作,Spring 提供了幾個註解作為 @RequestMapping 的簡化形式:

  • @GetMapping:用於處理 GET 請求
  • @PostMapping:用於處理 POST 請求
  • @PutMapping:用於處理 PUT 請求
  • @DeleteMapping:用於處理 DELETE 請求
  • @PatchMapping:用於處理 PATCH 請求

這些註解都相當於 @RequestMapping 的簡化版,直接指定了請求方法類型。

示例:

@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
    return userService.getUserById(id);
}

@PostMapping("/user/create")
public User createUser(@RequestBody User user) {
    return userService.createUser(user);
}

4、路徑變數和請求參數

4.1 @PathVariable

@PathVariable 註解用於從 URL 的路徑中提取數據,並將這些數據綁定到控制器方法的參數上。它通常與 URL 模板中的占位符 {} 一起使用。

1. 基本用法

1.1 單一路徑變數

在控制器方法中,可以使用 @PathVariable 註解來提取單個路徑變數。

示例:

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public String getUser(@PathVariable("id") String id) {
        return "User ID: " + id;
    }
}
  • 請求 URL:/users/123
  • id 變數將被設置為 123,結果:User ID: 123

1.2 多個路徑變數

可以在 URL 模板中使用多個路徑變數,併在方法中提取這些變數。

示例:

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}/details/{detailId}")
    public String getUserDetails(@PathVariable("id") String id,
                                 @PathVariable("detailId") String detailId) {
        return "User ID: " + id + ", Detail ID: " + detailId;
    }
}
  • 請求 URL:/users/123/details/456
  • id 變數將被設置為 123detailId 變數將被設置為 456,結果:User ID: 123, Detail ID: 456

2. URL 模板中的占位符

2.1 單層路徑變數

路徑變數用於 URL 模板中的單層路徑。

示例:

@GetMapping("/products/{productId}")
public String getProduct(@PathVariable("productId") String productId) {
    return "Product ID: " + productId;
}
  • URL:/products/789
  • productId 變數將被設置為 789

2.2 多層路徑變數

路徑變數可以用在 URL 的多層路徑中,提取不同層次的數據。

示例:

@GetMapping("/categories/{categoryId}/items/{itemId}")
public String getItem(@PathVariable("categoryId") String categoryId,
                      @PathVariable("itemId") String itemId) {
    return "Category ID: " + categoryId + ", Item ID: " + itemId;
}
  • URL:/categories/1/items/42
  • categoryId 變數將被設置為 1itemId 變數將被設置為 42

3. 路徑變數的名稱匹配

  • @PathVariable 的名稱需要與 URL 模板中的占位符名稱匹配。如果不匹配,會導致綁定失敗。

示例:

@GetMapping("/items/{itemId}")
public String getItem(@PathVariable("itemId") String id) {
    // 此處 id 是 itemId 的別名,實際名稱應與 URL 模板一致
    return "Item ID: " + id;
}

4.2 @RequestParam

@RequestParam 是 Spring MVC 中用於處理 HTTP 請求參數的註解。它用於從 HTTP 請求的查詢字元串或表單參數中提取數據。它可以用於處理 GETPOSTPUTDELETE 等請求方法中的參數。

補充:

查詢字元串 是附加在 URL 末尾的鍵值對,用於向伺服器傳遞參數。它通常用於 GET 請求,也可以用於其他請求類型。查詢字元串以 ? 開始,後面跟隨參數鍵值對,用 & 分隔多個參數。

查詢字元串的格式為:

http://example.com/resource?key1=value1&key2=value2

表單參數 是通過 HTML 表單提交的鍵值對,通常在 POST 請求中使用。表單參數可以通過 application/x-www-form-urlencodedmultipart/form-data 編碼發送,具體取決於表單的內容類型。

表單數據的格式在 application/x-www-form-urlencoded 格式中,表單數據類似於查詢字元串:

key1=value1&key2=value2

示例

HTML 表單:

<form action="/register" method="post">
    <input type="text" name="username" value="alice">
    <input type="number" name="age" value="30">
    <button type="submit">Register</button>
</form>
  • username 的值是 alice

  • age 的值是 30

  • 表單提交的數據:username=alice&age=30

  • 返回結果:Registered user: alice, Age: 30

總結

  • 查詢字元串 是在 URL 中附加的參數,用於 GET 請求。
  • 表單參數 是通過 HTML 表單提交的參數,通常用於 POST 請求。
  • 在 Spring MVC 中,@RequestParam 註解可以用於提取這兩種類型的請求參數。

1. 基本用法

1.1 從查詢字元串中獲取參數(GET 請求)

當使用 GET 請求時,參數通常附加在 URL 的查詢字元串中。

示例:

@RestController
public class UserController {

    @GetMapping("/greet")
    public String greet(@RequestParam("name") String name) {
        return "Hello, " + name + "!";
    }
}
  • 請求 URL:/greet?name=Alice
  • 結果:Hello, Alice!

1.2 從表單數據中獲取參數(POST 請求)

當使用 POST 請求時,參數通常是表單數據的一部分。

示例:

@RestController
public class UserController {

    @PostMapping("/register")
    public String register(@RequestParam("username") String username,
                           @RequestParam("password") String password) {
        return "Registered user: " + username;
    }
}
  • 表單數據:
    <form action="/register" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <button type="submit">Register</button>
    </form>
    

2. @RequestParam 的屬性

2.1 value 屬性

value(或 name)屬性指定請求參數的名稱。

示例:

@RequestParam(value = "name") String name
// 或
@RequestParam("name") String name

2.2 required 屬性

required 屬性用於指示參數是否是必需的。預設為 true,如果請求中沒有提供該參數,Spring 將拋出異常。如果設置為 false,則可以省略該參數。

示例:

@RequestParam(name = "name", required = false) String name
  • 如果請求中不包含 name 參數,name 將被設置為 null,而不是拋出異常。

2.3 defaultValue 屬性

defaultValue 屬性用於指定當請求參數缺失時的預設值。如果參數缺失,則使用預設值而不是拋出異常。

示例:

@RequestParam(name = "name", defaultValue = "Guest") String name
  • 如果請求中不包含 name 參數,name 將被設置為 "Guest"

3. 路徑變數與請求參數的區別

  • 路徑變數(Path Variables):用於從 URL 的路徑中提取數據。例如:/users/{id}
  • 請求參數(Request Parameters):用於從查詢字元串或表單數據中提取數據。例如:/users?id=1

示例:

@RestController
public class UserController {

    @GetMapping("/users/{id}")
    public String getUser(@PathVariable("id") String id) {
        return "User ID: " + id;
    }

    @GetMapping("/search")
    public String search(@RequestParam("query") String query) {
        return "Search query: " + query;
    }
}
  • /users/1 使用路徑變數,id 是路徑的一部分。
  • /search?query=spring 使用請求參數,query 是查詢字元串的一部分。

4. 組合使用

@RequestParam 可以與其他註解(如 @PathVariable@RequestBody)組合使用,處理複雜的請求。

示例:

@RestController
public class UserController {

    @PostMapping("/users/{id}")
    public String updateUser(@PathVariable("id") String id,
                             @RequestParam("name") String name,
                             @RequestParam(name = "email", required = false) String email) {
        return "Updated user ID: " + id + ", Name: " + name + ", Email: " + (email != null ? email : "Not provided");
    }
}

5. 支持的請求內容類型

  • application/x-www-form-urlencoded:傳統的表單提交方式。
  • multipart/form-data:用於文件上傳等複雜表單數據。
  • application/json:通常與 @RequestBody 結合使用,但在特定情況下也可以用於 @RequestParam

5、RESTful 風格

RESTful 風格一種基於資源和 HTTP 方法的 Web 服務設計方式,利用標準的 HTTP 動詞來處理資源操作,通過 JSON 等格式表示資源狀態。它具有可讀性高、靈活性強、標準化等優點,是現代 Web 開發中廣泛採用的 API 設計模式。

REST 代表 REpresentational State Transfer,意思是通過表現層狀態轉換進行系統交互。RESTful API 是基於 REST 原則設計的應用程式介面(API),常用於構建面向資源的網路服務。

1. RESTful 風格的核心概念

RESTful 風格主要圍繞以下幾個核心概念展開:

1.1 資源(Resource)

資源是 REST 的核心,表示網路上的數據或服務。每一個資源都通過一個唯一的 URI(Uniform Resource Identifier,統一資源標識符)來標識。例如:

  • http://example.com/users:表示用戶集合資源。
  • http://example.com/users/1:表示具體用戶資源,用戶 ID 為 1。

資源通常以 名詞 來命名,並且在 URI 中儘量避免使用動詞。

1.2 HTTP 方法

RESTful API 使用標準的 HTTP 方法來對資源執行不同的操作,每個方法都與一個特定的動作語義相關:

  • GET:從伺服器獲取資源。

    • 示例:GET /users 獲取所有用戶,GET /users/1 獲取 ID 為 1 的用戶。
  • POST:向伺服器創建一個新的資源。

    • 示例:POST /users 在用戶集合中創建一個新用戶。
  • PUT:更新伺服器上的資源(通常是替換整個資源)。

    • 示例:PUT /users/1 更新 ID 為 1 的用戶信息。
  • PATCH:部分更新伺服器上的資源(通常是更新資源的某些欄位)。

    • 示例:PATCH /users/1 更新 ID 為 1 的用戶的部分信息。
  • DELETE:從伺服器上刪除資源。

    • 示例:DELETE /users/1 刪除 ID 為 1 的用戶。

1.3 狀態表示(Representation)

當客戶端請求一個資源時,伺服器將該資源的當前狀態通過 狀態表示 發送給客戶端。通常,RESTful API 使用 JSONXML 來表示資源的狀態。例如,獲取用戶資源可能會返回這樣的 JSON 數據:

{
    "id": 1,
    "name": "Alice",
    "email": "[email protected]"
}

1.4 無狀態性(Stateless)

RESTful 服務是 無狀態的,這意味著每個請求都應該包含所有必要的信息,伺服器不會在不同請求之間保留任何客戶端的狀態。每次請求都是獨立的,伺服器不會依賴之前請求中的信息。

1.5 URI 的結構化

RESTful API 的 URI 通常是結構化的,以層級關係表示資源。例如:

  • /users:表示所有用戶。
  • /users/1:表示 ID 為 1 的用戶。
  • /users/1/orders:表示 ID 為 1 的用戶的訂單。

2. RESTful 風格的設計原則

RESTful API 設計遵循以下幾個原則:

2.1 資源的標識通過 URI

每個資源都應該有唯一的 URI,並且通過這個 URI 可以對資源進行操作。例如,用戶資源 /users 的 URI 是 /users,單個用戶資源 /users/1 的 URI 是 /users/1

2.2 使用 HTTP 方法明確資源操作

HTTP 方法(GET、POST、PUT、DELETE 等)應該明確表示對資源的操作,避免在 URI 中使用動詞。例如,不推薦使用 /getUser/deleteUser 這樣的 URI,應該使用 /users 並結合 HTTP 方法來實現。

2.3 使用 JSON 或 XML 表示資源

資源的狀態表示通常使用標準的格式,比如 JSON 或 XML,其中 JSON 更常用,因為它簡潔、易於解析。響應體中包含資源的狀態信息,並返回給客戶端。

2.4 無狀態的請求

每次客戶端與伺服器之間的交互都是獨立的,伺服器不保留客戶端的狀態。請求中的所有必要信息(如認證信息)都應該包含在每次請求中。

2.5 遵循標準的狀態碼

RESTful API 應該使用標準的 HTTP 狀態碼來反映操作的結果:

  • 200 OK:請求成功並返回數據。
  • 201 Created:成功創建資源。
  • 204 No Content:操作成功但不返回數據(如 DELETE 請求)。
  • 400 Bad Request:客戶端發送的請求無效(如參數錯誤)。
  • 401 Unauthorized:未經授權的請求。
  • 404 Not Found:請求的資源不存在。
  • 500 Internal Server Error:伺服器內部錯誤。

3. RESTful 風格的示例

假設我們設計一個管理用戶的 RESTful API,以下是一些操作和對應的 RESTful URL 和 HTTP 方法:

操作 HTTP 方法 URL 請求體 響應體
獲取所有用戶 GET /users 用戶列表 (JSON)
獲取特定用戶(ID = 1) GET /users/1 用戶信息 (JSON)
創建新用戶 POST /users 用戶信息 (JSON) 新建用戶信息
更新特定用戶(ID = 1) PUT /users/1 更新後的用戶信息 更新後的用戶信息
刪除特定用戶(ID = 1) DELETE /users/1

4. RESTful 與傳統 API 的對比

  • RESTful API:基於資源和 HTTP 方法,簡潔、標準化、易於理解和擴展。請求和響應體通常使用 JSON,客戶端和伺服器之間的通信是無狀態的。
  • 傳統 API:有時會在 URL 中包含動詞,例如 /getUser/deleteUser,並且可能不遵循 HTTP 方法的語義。

5. RESTful API 的優勢

  • 可讀性強:基於資源的設計和標準的 HTTP 方法,使得 API 直觀且易於理解。
  • 靈活性高:RESTful API 可以支持多種客戶端(如 Web、移動設備)。
  • 無狀態性:減少伺服器的負擔,因為它不需要保存客戶端的狀態。
  • 標準化:使用標準的 HTTP 協議,容易被各種工具和平臺集成。

6. RESTful API 的局限

  • 無狀態性限制:無狀態性雖然簡化了請求,但在一些需要持續狀態的操作(如登錄狀態)中需要額外設計。
  • 複雜查詢場景:對於複雜的查詢場景,RESTful API 可能需要設計額外的過濾機制,如分頁、排序、搜索等。

6、數據返回格式

6.1 Json

在前後端分離的模式下,數據的返回格式通常是 JSON(預設),Spring MVC 使用 Jackson 或 Gson 等庫來自動將 Java 對象轉換為 JSON 格式。

示例:

@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
    return new User(id, "Alice", 30);  // 自動返回 JSON 數據
}

返回的 JSON 數據格式如下:

{
  "id": 1,
  "name": "Alice",
  "age": 30
}

maven 官網 jackson 依賴
在這裡插入圖片描述

6.2 RequestEntity

RequestEntity 是 Spring 中用來封裝 HTTP 請求信息的類,它繼承自 HttpEntity,並擴展了對 HTTP 請求頭、HTTP 方法(如 GET、POST)、URL 等的支持。RequestEntity 可以用來在處理請求時,獲取完整的請求信息,例如請求頭、請求體、請求方法等。

核心屬性

  • HTTP 請求頭 (Headers):可以通過 RequestEntity 獲取請求中的 HTTP 頭信息。
  • HTTP 請求方法 (Method):可以通過 RequestEntity 知曉請求是 GET、POST、PUT、DELETE 等方法。
  • 請求 URLRequestEntity 包含完整的請求 URL,便於在處理請求時獲取 URL 參數。
  • 請求體 (Body):對於 POST、PUT 等帶有請求體的方法,可以通過 RequestEntity 獲取請求體中的數據。

RequestEntity 常用方法

  • getMethod():獲取 HTTP 請求方法。
  • getHeaders():獲取請求頭。
  • getBody():獲取請求體。
  • getUrl():獲取請求的 URL。

RequestEntity 示例

假設我們需要創建一個處理 POST 請求的介面,可以使用 RequestEntity 獲取請求的所有細節,包括請求頭、請求體和請求方法。

示例代碼:

@PostMapping("/process")
public ResponseEntity<String> handleRequestEntity(RequestEntity<String> requestEntity) {
    // 獲取請求方法
    HttpMethod method = requestEntity.getMethod();
    
    // 獲取請求頭
    HttpHeaders headers = requestEntity.getHeaders();
    
    // 獲取請求體
    String body = requestEntity.getBody();
    
    // 列印請求信息
    System.out.println("Method: " + method);
    System.out.println("Headers: " + headers);
    System.out.println("Body: " + body);
    
    return ResponseEntity.ok("Request processed successfully");
}

請求示例:

curl -X POST http://localhost:8080/process -H "Content-Type: text/plain" -d "Hello, Spring!"

輸出:

Method: POST
Headers: [Content-Type:"text/plain"]
Body: Hello, Spring!

在這個例子中,RequestEntity 提供了對請求的詳細信息,便於處理複雜的請求邏輯。

構造 RequestEntity

RequestEntity 也可以通過靜態工廠方法來創建,例如:

RequestEntity<String> requestEntity = RequestEntity
        .post(new URI("http://localhost:8080/process"))
        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .body("{\"key\": \"value\"}");

這裡我們通過 RequestEntity 構造了一個 POST 請求,設置了請求頭和請求體。

RequestEntity 的適用場景

  • 需要訪問 HTTP 請求的完整信息:當你需要獲取請求的所有細節(包括方法、頭、URL、體)時,RequestEntity 非常有用。
  • 複雜的 API 請求處理:在處理 REST API 請求時,它提供了獲取和操作請求細節的能力。

6.3 ResponseEntity

ResponseEntity 是 Spring 中用來封裝 HTTP 響應信息的類,它同樣繼承自 HttpEntity,擴展了對 HTTP 響應頭、響應狀態碼的支持。ResponseEntity 允許我們更靈活地設置返回給客戶端的響應,包括狀態碼、響應體、響應頭等。

核心屬性

  • HTTP 響應頭 (Headers):可以通過 ResponseEntity 設置或獲取返回給客戶端的 HTTP 頭信息。
  • HTTP 響應體 (Body):可以通過 ResponseEntity 設置返回給客戶端的響應數據。
  • HTTP 狀態碼 (Status Code):可以通過 ResponseEntity 設置 HTTP 響應的狀態碼(如 200 OK404 Not Found500 Internal Server Error 等)。

ResponseEntity 常用方法

  • status(HttpStatus):設置響應狀態碼。
  • body(Object):設置響應體內容。
  • header(String, String):設置響應頭。
  • ok():返回狀態碼 200 的響應。
  • notFound():返回狀態碼 404 的響應。
  • badRequest():返回狀態碼 400 的響應。

ResponseEntity 示例

假設我們需要處理一個文件上傳操作,並返回不同的響應狀態碼來表示上傳的結果:

示例代碼:

@PostMapping("/upload")
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("文件為空");
    }

    try {
        // 假設保存文件到指定路徑
        String uploadDir = "/path/to/upload/directory/";
        file.transferTo(new File(uploadDir + file.getOriginalFilename()));
        return ResponseEntity.ok("文件上傳成功");
    } catch (IOException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上傳失敗");
    }
}

請求示例:

curl -X POST -F "file=@/path/to/file.txt" http://localhost:8080/upload

可能的響應:

  • 成功時:200 OK,響應體為 "文件上傳成功"
  • 文件為空時:400 Bad Request,響應體為 "文件為空"
  • 出現伺服器錯誤時:500 Internal Server Error,響應體為 "文件上傳失敗"

在這個例子中,ResponseEntity 用來靈活地返回不同的狀態碼和消息。

構造 ResponseEntity

你可以通過靜態工廠方法來構建 ResponseEntity,例如:

ResponseEntity<String> response = ResponseEntity
        .status(HttpStatus.CREATED)
        .header(HttpHeaders.LOCATION, "/new-resource")
        .body("資源創建成功");

這個例子返回了一個 201 Created 的響應,同時設置了 Location 響應頭,指向新創建的資源路徑。

ResponseEntity 的適用場景

  • 需要靈活設置響應:當你需要自定義響應的狀態碼、頭、體時,ResponseEntity 提供了很好的靈活性。
  • RESTful API 響應:在 REST API 開發中,它是一個常見的選擇,便於構建符合 HTTP 標準的響應。

6.4 RequestEntityResponseEntity 的關係

RequestEntity 主要用於處理 HTTP 請求,幫助我們獲取請求的所有詳細信息;而 ResponseEntity 則用於構建和返回 HTTP 響應,幫助我們返回自定義的響應數據、頭和狀態碼。

常見組合使用場景

在 Spring MVC 的控制器方法中,你可以同時使用 RequestEntity 來獲取請求信息,使用 ResponseEntity 返迴響應。例如:

@PostMapping("/process-data")
public ResponseEntity<String> handleRequest(RequestEntity<String> requestEntity) {
    // 獲取請求體
    String requestBody = requestEntity.getBody();
    
    // 處理邏輯...
    
    // 返迴響應
    return ResponseEntity.ok("處理成功:" + requestBody);
}

7、文件的上傳和下載

在 Spring MVC 中,文件上傳和下載是常見的需求,通常通過 MultipartFileHttpServletResponse 等組件來處理。下麵我們來詳細講解文件上傳和下載的實現。

1. 文件上傳

1.1 使用 MultipartFile 實現文件上傳

Spring MVC 提供了 MultipartFile 介面來處理文件上傳。為了支持文件上傳,首先需要在項目的配置文件中啟用對多部分請求的支持。

1.2 配置 Spring Boot 以支持文件上傳

application.propertiesapplication.yml 中配置最大文件上傳大小:

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=15MB
  • max-file-size:限制單個文件的大小。
  • max-request-size:限制整個請求的大小。

1.3 文件上傳的 Controller 示例

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;

import java.io.File;
import java.io.IOException;

@RestController
@RequestMapping("/files")
public class FileUploadController {

    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        // 檢查文件是否為空
        if (file.isEmpty()) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("文件為空");
        }

        // 獲取文件名
        String fileName = file.getOriginalFilename();

        try {
            // 文件保存路徑(可以根據需求修改)
            String uploadDir = "/path/to/upload/directory/";
            File dest = new File(uploadDir + fileName);

            // 保存文件到伺服器
            file.transferTo(dest);

            return ResponseEntity.ok("文件上傳成功:" + fileName);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上傳失敗");
        }
    }
}

1.4 前端文件上傳表單示例

<form method="POST" enctype="multipart/form-data" action="/files/upload">
    <input type="file" name="file"/>
    <button type="submit">上傳文件</button>
</form>

在這個例子中,文件上傳表單使用 enctype="multipart/form-data" 進行編碼,使文件能被正確地傳輸到伺服器。

2. 文件下載

文件下載通常通過 HttpServletResponse 來設置響應頭並將文件的內容寫入到輸出流中。

2.1 文件下載的 Controller 示例

import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

@RestController
@RequestMapping("/files")
public class FileDownloadController {

    @GetMapping("/download/{fileName}")
    public void downloadFile(@PathVariable("fileName") String fileName, HttpServletResponse response) {
        // 文件存儲路徑
        String filePath = "/path/to/upload/directory/" + fileName;
        File file = new File(filePath);

        if (!file.exists()) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 設置響應頭
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        response.setContentLengthLong(file.length());

        try (FileInputStream fis = new FileInputStream(file);
             OutputStream os = response.getOutputStream()) {

            // 將文件數據寫入輸出流
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
        } catch (IOException e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}

2.2 前端文件下載鏈接示例

<a href="/files/download/sample.txt">下載文件</a>

在這個示例中,當用戶點擊鏈接時,瀏覽器會發出下載請求,並觸發 downloadFile 方法。該方法會將伺服器上的文件內容以流的形式發送到客戶端。

3. 文件上傳與下載的詳細解釋

3.1 文件上傳

  • MultipartFile:Spring MVC 提供的介面,用於處理上傳的文件。你可以通過 @RequestParam("file") 獲取上傳的文件。
  • transferToMultipartFile 提供的一個方法,用於將上傳的文件保存到指定路徑。
  • 表單的 enctype:文件上傳表單的 enctype="multipart/form-data",它允許文件和其他數據一起被髮送。

3.2 文件下載

  • HttpServletResponse:用於生成 HTTP 響應,包括設置響應頭和寫入響應體。
  • Content-Type:指定響應的內容類型為 application/octet-stream,表示該響應是一個二進位流。
  • Content-Disposition:用於指示瀏覽器將響應內容作為附件下載,而不是直接顯示。例如,attachment; filename="file.txt" 會強制瀏覽器下載文件,並將其命名為 file.txt
  • 流操作:通過 FileInputStream 讀取文件內容,並通過 OutputStream 將內容寫入響應體中。

4. 註意事項

  • 文件路徑安全性:在處理文件路徑時,確保不要暴露伺服器文件系統的敏感信息。建議使用文件存儲服務或資料庫來管理上傳的文件路徑。
  • 文件上傳大小限制:在實際生產環境中,除了在 Spring 配置文件中設置文件上傳大小限制之外,也可以通過 Web 伺服器(如 Tomcat、Nginx)進行限制。
  • 異常處理:在文件上傳和下載過程中,可能會出現各種異常(如文件不存在、許可權問題等),應對這些異常進行適當的處理。

5. 總結

Spring MVC 提供了簡潔的方式來處理文件上傳和下載。通過使用 MultipartFileHttpServletResponse,開發者可以輕鬆實現文件上傳和下載的功能。

8、攔截器

在 Spring MVC 中,攔截器(Interceptor)是一個用於在請求處理的不同階段進行攔截和處理的機制。攔截器可以攔截進入控制器之前、控制器執行之後以及請求完成之後的操作,並允許開發者在這些過程中自定義邏輯。攔截器的使用使得我們可以實現諸如許可權驗證、日誌記錄、數據預處理、響應數據後處理等功能。

攔截器類似於過濾器(Filter),但與過濾器不同的是,攔截器專註於與 Spring MVC 的請求處理流程結合得更緊密,並且能夠直接訪問 Spring 的上下文和數據。


1. 攔截器的工作原理

攔截器通過實現 HandlerInterceptor 介面來實現,它定義了三個主要的回調方法,分別對應請求處理的不同階段:

三個主要方法:

  1. preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

    • 時機:在請求到達控制器之前執行(即在執行控制器方法之前)。
    • 作用:用於處理一些前置邏輯,如認證、許可權檢查、日誌記錄等。
    • 返回值:返回 true 時繼續執行下一個攔截器或進入控制器方法,返回 false 則中斷請求處理,不會繼續執行後續攔截器或控制器。
  2. postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)

    • 時機:在控制器方法執行之後,視圖渲染之前執行。
    • 作用:可以修改控制器返回的 ModelAndView,對數據進行二次處理,或者決定渲染的視圖。
  3. afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

    • 時機:在整個請求完成之後(視圖渲染完成後)執行。
    • 作用:用於進行一些資源清理、日誌記錄、異常處理等工作。

2. 如何實現攔截器

步驟一:創建攔截器類

攔截器類需要實現 HandlerInterceptor 介面。

示例攔截器實現

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor {

    // 進入控制器之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle: 請求前檢查許可權...");
        
        // 可以進行許可權驗證邏輯,返回 false 則攔截請求
        String authToken = request.getHeader("Authorization");
        if (authToken == null || !authToken.equals("VALID_TOKEN")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false; // 攔截請求
        }

        return true; // 繼續處理請求
    }

    // 控制器執行後,視圖渲染前
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle: 可以修改 ModelAndView...");
    }

    // 請求結束,視圖渲染完成後
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion: 資源清理...");
    }
}

步驟二:註冊攔截器

創建好攔截器類後,我們需要將其註冊到 Spring 的攔截器鏈中。通常,我們通過配置類來註冊攔截器。

示例配置

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 註冊攔截器
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**") // 攔截所有路徑
                .excludePathPatterns("/login", "/public/**"); // 排除某些路徑
    }
}

在這個配置中,攔截器 MyInterceptor 被註冊到攔截器鏈中,並指定攔截所有請求路徑(/**),同時排除 /login/public/** 的路徑不被攔截。


3. 攔截器與過濾器的區別

相同點

  • 兩者都可以在請求到達控制器之前和請求完成之後執行邏輯。
  • 都可以用於執行諸如許可權驗證、日誌記錄、性能監控等功能。

不同點

攔截器 (Interceptor) 過濾器 (Filter)
作用於 Spring MVC 的請求處理流程,與 Spring 的上下文結合緊密。 作用於整個 web 應用的請求鏈,與 Spring 無關。
可以獲取控制器信息,並且可以操作 ModelAndView 只能操作原生的 HttpServletRequestHttpServletResponse,不涉及 Spring MVC 的特性。
實現 HandlerInterceptor 介面。 實現 javax.servlet.Filter 介面。
常用於 RESTful API 中的數據預處理、後處理。 常用於全局的過濾、編碼處理、安全檢查等。

4. 攔截器的常見應用場景

1. 許可權驗證

通過 preHandle 方法在請求進入控制器之前攔截請求,進行許可權驗證或認證處理。如果驗證失敗,可以返回相應的錯誤響應,終止請求繼續執行。

示例:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String authToken = request.getHeader("Authorization");
    if (authToken == null || !isValidToken(authToken)) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
        return false;
    }
    return true;
}

2. 日誌記錄

preHandleafterCompletion 中記錄請求的日誌,包括請求的 URL、參數、處理時間等。

示例:

long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    long startTime = (Long) request.getAttribute("startTime");
    long endTime = System.currentTimeMillis();
    System.out.println("Request URL: " + request.getRequestURL() + " - Time Taken: " + (endTime - startTime) + "ms");
}

3. 數據預處理

preHandle 中進行數據的預處理,例如對請求參數進行格式化、數據補充等。

示例:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String param = request.getParameter("date");
    if (param != null) {
        // 預處理日期參數
        LocalDate date = LocalDate.parse(param, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        request.setAttribute("formattedDate", date);
    }
    return true;
}

4. 數據後處理

postHandle 中對控制器返回的 ModelAndView 進行修改,添加公共的頁面元素或數據。

示例:

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    if (modelAndView != null) {
        modelAndView.addObject("footerMessage", "版權所有 © 2024");
    }
}

5. 攔截器執行順序

如果有多個攔截器時,它們的執行順序是根據註冊時的順序來決定的。所有 preHandle 方法按照順序依次執行,所有 postHandleafterCompletion 方法按照相反的順序執行。

假設有兩個攔截器 Interceptor1Interceptor2,它們的執行順序如下:

  • Interceptor1.preHandle() -> Interceptor2.preHandle() -> 控制器方法 -> Interceptor2.postHandle() -> Interceptor1.postHandle() -> Interceptor2.afterCompletion() -> Interceptor1.afterCompletion()

6. 總結

  • HandlerInterceptor 是 Spring MVC 中的攔截器介面,它通過 preHandlepostHandleafterCompletion 三個方法來攔截請求的不同階段。
  • 攔截器通常用於許可權驗證、日誌記錄、數據預處理和後處理等場景。
  • 與過濾器相比,攔截器與 Spring MVC 緊密集成,能夠獲取

9、跨域支持(CORS)

前後端分離時,前端和後端通常部署在不同的伺服器上,這會導致跨域問題。Spring MVC 提供了多種跨域配置方式,最常見的是使用 @CrossOrigin 註解。

示例:

@RestController
@CrossOrigin(origins = "http://localhost:8080")
@RequestMapping("/api/users")
public class UserController {
    // 控制器方法
}

或者可以在全局配置跨域:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET", "POST", "PUT", "DELETE");
    }
}

8、常見開發模式

在前後端分離中,Spring MVC 通常與 RESTful API 模式結合使用,前端通過 HTTP 方法調用後端的 API 介面。後端僅負責數據處理和業務邏輯,而前端負責用戶界面、數據展示和交互。

  1. 前端使用 Axios 或 Fetch 調用後端 API:
    前端通過 AJAX 向後端發送請求:

    axios.get('/api/users/1')
        .then(response => {
            console.log(response.data);
        })
        .catch(error => {
            console.error(error);
        });
    
  2. 後端返回 JSON 數據:
    後端 Controller 處理請求並返回 JSON 數據,前端接收數據後進行頁面更新。

9、總結

在前後端分離模式下,Spring MVC 的主要職責是處理請求、進行業務邏輯處理、返回 JSON 或 XML 格式的數據。其核心組件如 @RestController@RequestMapping@ResponseBody 等,簡化了 RESTful API 的開發流程。此外,Spring MVC 還提供了強大的異常處理機制和跨域支持,使前後端分離的開發更加順暢。


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

-Advertisement-
Play Games
更多相關文章
  • 1. 基本信息 軟體設計的要素 丹尼爾·傑克遜著 浙江教育出版社,2024年5月出版 1.1. 讀薄率 書籍總字數236千字,筆記總字數18853字。 讀薄率18853÷236000≈8% 1.2. 讀厚方向 構建可擴展分散式系統方法與實踐 設計模式:可復用面向對象軟體的基礎 程式員修煉之道:通向務 ...
  • 1. 概念完整性 1.1. 當概念組合成一個軟體時,它們可以同步以便協調行為 1.1.1. 同步可能會消除一個概念的某些行為,但決不會添加與該概念的規範不一致的新行為 1.1.2. 在使用概念設計軟體時,即使你沒有精確定義同步,至少要說服自己,概念之間的每次交互至少在原則上都可以被視為同步 1.2. ...
  • 1. 字元集與字元編碼 1.1. 字元集 1.2. 字元編碼 1.3. 兩者的關係 2. 字元編碼的發展歷史 2.1. 第一個階段 ASCII編碼 2.1.1. ASCII 2.1.2. EASCII 1. 字元集與字元編碼 1.1. 字元集 字元集(Charcater Set或Charset): ...
  • 目錄C語言的四種取整方式:零向取整trunc函數(C99)trunc的使用地板取整floor函數的使用向上取整ceil函數的使用四捨五入round函數(C99)round函數的使用四種取整方式演示 C語言的四種取整方式: 零向取整 如圖: 可以發現C語言a和b的取整方式都不是四捨五入,而是直接捨棄小 ...
  • 線程安全的級別用於描述在多線程環境下,某個對象或類在處理併發訪問時的安全性程度。它幫助開發者瞭解不同數據結構或代碼在多線程中使用時,需要什麼樣的處理措施,以確保數據一致性和避免競態條件(數據競爭)。 線程安全性可以分為不同的級別,取決於對併發訪問的控制和保證數據一致性的程度。以下是線程安全的幾個主要 ...
  • 泛型是一種類型參數。將數據的類型,當做一個參數。 1. 泛型的作用 在創建集合對象的時候,限定集合存儲元素的類型; 在編譯的時候,就進行類型檢查; 2. 泛型的使用規則 如果不指定泛型,預設是Object類型 泛型擦除:泛型應用於代碼編譯期,程式運行的後,泛型就被擦除了。即運行期,泛型被擦除。 同時 ...
  • Spring 框架既可以從 廣義 和 狹義 兩個角度理解,下麵講解這兩個層面的概念: (本文主要講解的是狹義上的spring,廣義上的簡單概括) 1、spring 的含義 1. 廣義上的 Spring 從廣義上講,Spring 是一個涵蓋多個模塊的企業級應用開發框架,它提供了從基礎架構到複雜企業應用 ...
  • 題目描述 給你二叉樹的根節點 root 和一個表示目標和的整數 targetSum 。判斷該樹中是否存在 根節點到葉子節點 的路徑,這條路徑上所有節點值相加等於目標和 targetSum 。如果存在,返回 true ;否則,返回 false 。 葉子節點 是指沒有子節點的節點。 解題思路 我們這題採 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...