@elf-express/admin-net-mcp 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/index.js +83 -88
  2. package/knowledge/Furion_Teaching_Manual/04-1-/351/205/215/347/275/256.md +442 -0
  3. package/knowledge/Furion_Teaching_Manual/04-2-/351/201/270/351/240/205.md +363 -0
  4. package/knowledge/Furion_Teaching_Manual/05-1-/345/213/225/346/205/213WebAPI.md +825 -0
  5. package/knowledge/Furion_Teaching_Manual/05-2-HttpContext.md +217 -0
  6. package/knowledge/Furion_Teaching_Manual/05-3-/347/257/251/351/201/270/345/231/250/346/224/224/346/210/252/345/231/250AOP.md +581 -0
  7. package/knowledge/Furion_Teaching_Manual/05-4-/350/253/213/346/261/202/347/250/275/346/240/270/346/227/245/350/252/214.md +129 -0
  8. package/knowledge/Furion_Teaching_Manual/05-5-/344/270/255/344/273/213/350/273/237/351/253/224Middleware.md +328 -0
  9. package/knowledge/Furion_Teaching_Manual/05-6-Vue-React-Angular/344/273/213/351/235/242/344/273/243/347/220/206.md +317 -0
  10. package/knowledge/Furion_Teaching_Manual/06-1/350/246/217/347/257/204/345/214/226/346/216/245/345/217/243.md +1458 -0
  11. package/knowledge/Furion_Teaching_Manual/06-2/347/254/254/344/270/211/346/226/271API_Scalar.md +91 -0
  12. package/knowledge/Furion_Teaching_Manual/07-/345/217/213/345/245/275/344/276/213/345/244/226/350/231/225/347/220/206.md +511 -0
  13. package/knowledge/Furion_Teaching_Manual/08-1-/350/263/207/346/226/231/351/251/227/350/255/211/345/237/272/347/244/216/344/275/277/347/224/250.md +587 -0
  14. package/knowledge/Furion_Teaching_Manual/10-1-SqlSugar/346/225/264/345/220/210.md +336 -0
  15. package/knowledge/Furion_Teaching_Manual/11-SaaS /345/244/232/347/247/237/346/210/266/347/255/206/350/250/230.md" +271 -0
  16. package/knowledge/Furion_Teaching_Manual/12-furion-dependency-injection.md +408 -0
  17. package/knowledge/Furion_Teaching_Manual/13-/347/211/251/344/273/266/350/263/207/346/226/231/346/230/240/345/260/204/357/274/210Mapster/357/274/211.md +162 -0
  18. package/knowledge/Furion_Teaching_Manual/14-/345/210/206/345/270/203/345/274/217/347/274/223/345/255/230.md +311 -0
  19. package/knowledge/Furion_Teaching_Manual/15-/345/256/211/345/205/250/351/211/264/346/235/203.md +832 -0
  20. package/knowledge/Furion_Teaching_Manual/17-/346/252/242/350/246/226/347/257/204/346/234/254/345/274/225/346/223/216.md +327 -0
  21. package/knowledge/Furion_Teaching_Manual/18-/346/227/245/350/252/214/350/250/230/351/214/204.md +639 -0
  22. package/knowledge/Furion_Teaching_Manual/19-1-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/345/237/272/347/244/216/344/275/277/347/224/250.md +621 -0
  23. package/knowledge/Furion_Teaching_Manual/19-2-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/351/200/262/351/232/216/346/214/207/345/215/227.md +928 -0
  24. package/knowledge/Furion_Teaching_Manual/19-3-HTTP/351/201/240/347/253/257/350/253/213/346/261/202/345/270/270/350/246/213/345/225/217/351/241/214.md +362 -0
  25. package/knowledge/Furion_Teaching_Manual/20-/350/263/207/346/226/231/345/212/240/350/247/243/345/257/206.md +286 -0
  26. package/knowledge/Furion_Teaching_Manual/20-/351/231/204/351/214/204-KSort/350/263/207/346/226/231/347/260/275/345/220/215/345/256/214/346/225/264/345/216/237/345/247/213/347/242/274.md +305 -0
  27. package/knowledge/Furion_Teaching_Manual/21-/345/205/250/347/220/203/345/214/226/345/222/214/346/234/254/345/234/260/345/214/226.md +342 -0
  28. package/knowledge/Furion_Teaching_Manual/22-/344/272/213/344/273/266/345/214/257/346/265/201/346/216/222EventBus.md +448 -0
  29. package/knowledge/Furion_Teaching_Manual/23-JSON/345/272/217/345/210/227/345/214/226.md +340 -0
  30. package/knowledge/Furion_Teaching_Manual/24-/345/215/263/346/231/202/351/200/232/350/250/212SignalR.md +247 -0
  31. package/knowledge/Furion_Teaching_Manual/25-/350/274/224/345/212/251/350/247/222/350/211/262/346/234/215/345/213/231WorkerService.md +295 -0
  32. package/knowledge/Furion_Teaching_Manual/26-1-/346/216/222/347/250/213/344/275/234/346/245/255/345/256/232/346/231/202/344/273/273/345/213/231.md +631 -0
  33. package/knowledge/Furion_Teaching_Manual/26-2-Cron/350/241/250/351/201/224/345/274/217.md +203 -0
  34. package/knowledge/Furion_Teaching_Manual/26-3-/344/273/273/345/213/231/344/275/207/345/210/227TaskQueue.md +215 -0
  35. package/knowledge/Furion_Teaching_Manual/27-/345/210/206/346/225/243/345/274/217ID/347/224/237/346/210/220.md +86 -0
  36. package/knowledge/Furion_Teaching_Manual/28-/346/250/241/347/265/204/345/214/226/351/226/213/347/231/274.md +68 -0
  37. package/knowledge/Furion_Teaching_Manual/29-/346/265/201/350/256/212/347/211/251/344/273/266Clay.md +313 -0
  38. package/knowledge/Furion_Teaching_Manual/30-/350/204/253/346/225/217/350/231/225/347/220/206/357/274/210Sensitive Detection).md" +168 -0
  39. package/knowledge/Furion_Teaching_Manual/32-/346/234/203/350/251/261/345/222/214/347/213/200/346/205/213/347/256/241/347/220/206.md +147 -0
  40. package/knowledge/Furion_Teaching_Manual/33-IPC/347/250/213/345/272/217/351/200/232/350/250/212.md +98 -0
  41. package/knowledge/Furion_Teaching_Manual/34-2-Docker/351/203/250/347/275/262.md +313 -0
  42. package/knowledge/Furion_Teaching_Manual/34-3-Nginx/351/203/250/347/275/262.md +157 -0
  43. package/knowledge/Furion_Teaching_Manual/34-8-WindowsService/351/203/250/347/275/262.md +112 -0
  44. package/knowledge/Furion_Teaching_Manual/35-1-Docker/347/222/260/345/242/203/346/214/201/347/272/214/351/203/250/347/275/262Jenkins.md +169 -0
  45. package/knowledge/Furion_Teaching_Manual/36-1-/345/226/256/345/205/203/346/270/254/350/251/246/346/225/264/345/220/210/346/270/254/350/251/246.md +275 -0
  46. package/knowledge/Furion_Teaching_Manual/36-3-/345/237/272/346/272/226/346/270/254/350/251/246Benchmarking.md +80 -0
  47. package/knowledge/attributes.md +153 -0
  48. package/knowledge/config.md +147 -0
  49. package/knowledge/entity.md +115 -0
  50. package/knowledge/event.md +124 -0
  51. package/knowledge/plugin.md +136 -0
  52. package/knowledge/service.md +146 -0
  53. package/knowledge/sqlsugar.md +172 -0
  54. package/knowledge/vue-typescript.md +338 -0
  55. package/package.json +3 -2
@@ -0,0 +1,621 @@
1
+ # 19.Ⅰ HTTP 遠端請求 — 基礎使用
2
+
3
+ > **版本說明**:以下內容僅適用於 Furion 4.9.6+ 版本(採用 HttpAgent 請求代理),且不支援 .NET8 以下版本。
4
+
5
+ ---
6
+
7
+ ## 19.1 概述
8
+
9
+ HTTP 遠端請求是指客戶端透過 HTTP 協定向遠端伺服器傳送請求以取得所需資源的過程。應用場景包括:資源取得、資料抓取、檔案傳輸、API 對接、系統整合、微服務通訊、負載均衡、壓力測試、請求代理等。
10
+
11
+ > **安裝包說明**:Furion 框架已內建該功能。若使用非 Furion 框架,可安裝 `HttpAgent`(任何 .NET 應用)或 `HttpAgent.AspNetCore`(Web 應用)。
12
+
13
+ ---
14
+
15
+ ## 19.2 快速入門
16
+
17
+ ### 註冊服務
18
+
19
+ ```csharp
20
+ services.AddHttpRemote();
21
+ // 或 builder.Services.AddHttpRemote();
22
+ ```
23
+
24
+ ### 注入使用
25
+
26
+ ```csharp
27
+ public class YourService(IHttpRemoteService httpRemoteService)
28
+ {
29
+ // 使用 httpRemoteService
30
+ }
31
+ ```
32
+
33
+ 非相依性注入環境:
34
+
35
+ ```csharp
36
+ // Furion 框架
37
+ var httpRemoteService = App.GetRequiredService<IHttpRemoteService>();
38
+
39
+ // Console/WinForms/WPF
40
+ var result = await HttpRemoteClient.Service.GetAsStringAsync("https://furion.net/");
41
+ ```
42
+
43
+ ### 19.2.1 取得網站內容
44
+
45
+ ```csharp
46
+ // 最簡方式
47
+ var content = await httpRemoteService.GetAsStringAsync("https://furion.net");
48
+
49
+ // 建構器方式
50
+ var content = await httpRemoteService.SendAsStringAsync(HttpRequestBuilder.Get("https://furion.net"));
51
+
52
+ // 泛型方式
53
+ var content = await httpRemoteService.SendAsAsync<string>(HttpRequestBuilder.Get("https://furion.net"));
54
+
55
+ // 取得 HttpRemoteResult<T>
56
+ var result = await httpRemoteService.SendAsync<string>(HttpRequestBuilder.Get("https://furion.net"));
57
+ var content = result.Result;
58
+
59
+ // 取得 HttpResponseMessage
60
+ var response = await httpRemoteService.SendAsync(HttpRequestBuilder.Get("https://furion.net"));
61
+ var content = await response.Content.ReadAsStringAsync();
62
+ ```
63
+
64
+ ### 19.2.2 攜帶請求資料
65
+
66
+ ```csharp
67
+ var content = await httpRemoteService.PostAsAsync<YourRemoteModel>(
68
+ "https://localhost:7044/HttpRemote/AddModel",
69
+ builder => builder
70
+ .WithQueryParameters(new { query1 = 1, query2 = "furion" })
71
+ .SetJsonContent(new { id = 1, name = "furion" }));
72
+ ```
73
+
74
+ ### 19.2.3 Form 表單提交
75
+
76
+ **multipart/form-data:**
77
+
78
+ ```csharp
79
+ var content = await httpRemoteService.PostAsAsync<YourRemoteFormResult>(
80
+ "https://localhost:7044/HttpRemote/AddForm?id=1",
81
+ builder => builder.SetMultipartContent(multipart => multipart
82
+ .AddJson(new { id = 1, name = "furion" })
83
+ .AddFileAsStream(@"C:\Workspaces\httptest.jpg", "file")));
84
+ ```
85
+
86
+ `SetMultipartContent` 支援的方法包括:`AddJson`、`AddFormItem`、`AddHtml`、`AddXml`、`AddText`、`AddObject`、`AddFileFromRemote`、`AddFileFromBase64String`、`AddFileAsStream`、`AddFileWithProgressAsStream`、`AddFileAsByteArray`、`AddFile`(MultipartFile/IFormFile)、`AddFiles`、`AddStream`、`AddByteArray`、`Add`。
87
+
88
+ **application/x-www-form-urlencoded:**
89
+
90
+ ```csharp
91
+ var content = await httpRemoteService.PostAsAsync<YourRemoteModel>(
92
+ "https://localhost:7044/HttpRemote/AddURLForm",
93
+ builder => builder.SetFormUrlEncodedContent(new { id = 1, name = "furion" }));
94
+
95
+ // 使用 StringContent 解決編碼問題
96
+ builder.SetFormUrlEncodedContent(new { id = 1, name = "furion" }, useStringContent: true);
97
+ ```
98
+
99
+ ### 19.2.4 下載網路資源
100
+
101
+ ```csharp
102
+ var result = await httpRemoteService.DownloadFileAsync(
103
+ "https://example.com/file.exe",
104
+ @"C:\Workspaces\",
105
+ async progress =>
106
+ {
107
+ progress.UpdateConsoleProgress(); // 主控台進度條
108
+ await Task.CompletedTask;
109
+ },
110
+ fileExistsBehavior: FileExistsBehavior.Overwrite);
111
+ ```
112
+
113
+ `FileExistsBehavior`:`CreateNew`(預設,已存在則拋例外)、`Overwrite`(覆蓋)、`Skip`(跳過)。
114
+
115
+ `FileTransferProgress` 屬性:`FilePath`、`FileName`、`FileSize`、`Transferred`、`PercentageComplete`、`TransferRate`、`TimeElapsed`、`EstimatedTimeRemaining`。
116
+
117
+ ### 19.2.5 上傳檔案資源(OSS)
118
+
119
+ **Form 表單方式:**
120
+
121
+ ```csharp
122
+ await httpRemoteService.PostAsync("https://localhost:7044/HttpRemote/AddFile",
123
+ builder => builder.SetMultipartContent(multipart => multipart
124
+ .AddFileAsStream(@"C:\Workspaces\httptest.jpg", "file")));
125
+ ```
126
+
127
+ **非 Form 表單方式(OSS):**
128
+
129
+ ```csharp
130
+ var fileStream = File.OpenRead("檔案路徑");
131
+ await httpRemoteService.PutAsync("https://localhost:7044/HttpRemote/AddFile",
132
+ builder => builder.SetContent(fileStream));
133
+ ```
134
+
135
+ **UploadFile 擴充方法(帶進度):**
136
+
137
+ ```csharp
138
+ await httpRemoteService.UploadFileAsync(
139
+ "https://localhost:7044/HttpRemote/AddFile",
140
+ @"C:\Workspaces\httptest.jpg", "file",
141
+ async progress =>
142
+ {
143
+ progress.UpdateConsoleProgress();
144
+ await Task.CompletedTask;
145
+ });
146
+ ```
147
+
148
+ ### 19.2.6 HTTP 宣告式請求(代理方式)
149
+
150
+ ```csharp
151
+ public interface IHttpService : IHttpDeclarative
152
+ {
153
+ [Get("https://furion.net")]
154
+ Task<string> GetWebSiteContent();
155
+
156
+ [Post("https://localhost:7044/HttpRemote/AddModel")]
157
+ [Query("query1", 1)]
158
+ Task<YourRemoteModel> PostData([Query(AliasAs = "query2")] string param,
159
+ [Body(MediaTypeNames.Application.Json)] object data);
160
+
161
+ [Post("https://localhost:7044/HttpRemote/AddForm?id=1")]
162
+ Task<YourRemoteFormResult> PostForm(Action<HttpMultipartFormDataBuilder> multipart);
163
+ }
164
+
165
+ // 註冊
166
+ services.AddHttpRemote(builder =>
167
+ {
168
+ builder.AddHttpDeclarative<IHttpService>();
169
+ // 或掃描程式集批次註冊(推薦)
170
+ // builder.AddHttpDeclarativesFromAssemblies([Assembly.GetEntryAssembly()]);
171
+ });
172
+ ```
173
+
174
+ ### 19.2.7 請求分析工具 ✨
175
+
176
+ ```csharp
177
+ await httpRemoteService.GetAsync("https://furion.net",
178
+ builder => builder.Profiler());
179
+
180
+ // 全域啟用
181
+ services.AddHttpClient(string.Empty).AddProfilerDelegatingHandler();
182
+
183
+ // 生產環境停用
184
+ services.AddHttpClient(string.Empty)
185
+ .AddProfilerDelegatingHandler(disableInProduction: true);
186
+ ```
187
+
188
+ > ⚠️ 生產環境應停用請求分析工具。
189
+
190
+ ### 19.2.8 新增授權憑證
191
+
192
+ ```csharp
193
+ // JWT
194
+ builder.AddJwtBearerAuthentication("your token");
195
+
196
+ // Basic
197
+ builder.AddBasicAuthentication("username", "password");
198
+
199
+ // Digest
200
+ builder.AddDigestAuthentication("username", "password");
201
+
202
+ // 自訂
203
+ builder.AddAuthentication(new AuthenticationHeaderValue("X-Token", "your token"));
204
+ ```
205
+
206
+ 全域授權可透過自訂 `DelegatingHandler` 實現。
207
+
208
+ ### 19.2.9 設定 Cookie
209
+
210
+ ```csharp
211
+ // 單次請求
212
+ builder.WithCookie("name", "value").WithCookies(new { k1 = "v1", k2 = "v2" });
213
+
214
+ // HttpClient 全域設定(自動登入)
215
+ var cookieContainer = new CookieContainer();
216
+ services.AddHttpClient(string.Empty)
217
+ .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
218
+ {
219
+ CookieContainer = cookieContainer,
220
+ UseCookies = true,
221
+ AllowAutoRedirect = true
222
+ });
223
+ ```
224
+
225
+ ### 19.2.10 例外處理(例外抑制)
226
+
227
+ ```csharp
228
+ // 抑制所有例外
229
+ builder.SuppressExceptions();
230
+
231
+ // 僅抑制特定例外
232
+ builder.SuppressExceptions([typeof(TimeoutException), typeof(TaskCanceledException)]);
233
+
234
+ // 抑制但仍捕獲例外資訊
235
+ builder.SuppressExceptions()
236
+ .SetOnRequestFailed((ex, res) => Console.WriteLine(ex.Message));
237
+ ```
238
+
239
+ ### 19.2.11 壓力與模擬測試
240
+
241
+ ```csharp
242
+ var result = await httpRemoteService.StressTestHarnessAsync("https://furion.net/");
243
+ Console.WriteLine(result.ToString());
244
+
245
+ // 自訂參數
246
+ var result = await httpRemoteService.SendAsync(
247
+ HttpRequestBuilder.StressTestHarness("https://furion.net/")
248
+ .SetNumberOfRequests(1000)
249
+ .SetNumberOfRounds(5)
250
+ .SetMaxDegreeOfParallelism(500));
251
+ ```
252
+
253
+ ### 19.2.12 長輪詢 Long Polling
254
+
255
+ ```csharp
256
+ await httpRemoteService.LongPollingAsync("https://localhost:7044/HttpRemote/LongPolling",
257
+ async (responseMessage, token) =>
258
+ {
259
+ Console.WriteLine(await responseMessage.Content.ReadAsStringAsync(token));
260
+ await Task.CompletedTask;
261
+ }, cancellationToken: cancellationToken);
262
+ ```
263
+
264
+ ### 19.2.13 Server-Sent Events 單向通訊
265
+
266
+ ```csharp
267
+ await httpRemoteService.ServerSentEventsAsync("https://localhost:7044/HttpRemote/Events",
268
+ async (data, token) =>
269
+ {
270
+ Console.WriteLine(data.Data.ToString());
271
+ await Task.CompletedTask;
272
+ }, cancellationToken: cancellationToken);
273
+ ```
274
+
275
+ `ServerSentEventsData` 屬性:`Event`、`Data`、`Id`、`Retry`、`CustomFields`。
276
+
277
+ > ⚠️ 發送 SSE 請求時應關閉請求分析工具。
278
+
279
+ ### 19.2.14 WebSocket 雙工通訊
280
+
281
+ ```csharp
282
+ using var webSocketClient = new WebSocketClient("wss://localhost:7044/ws");
283
+
284
+ webSocketClient.Connected += (sender, s) => Console.WriteLine("連線成功");
285
+ webSocketClient.TextReceived += (sender, s) => Console.WriteLine(s.Message);
286
+
287
+ await webSocketClient.ConnectAsync(cancellationToken);
288
+ await webSocketClient.SendAsync("Hello", cancellationToken: cancellationToken);
289
+ await webSocketClient.CloseAsync(cancellationToken);
290
+ ```
291
+
292
+ ### 19.2.15 HttpContext 轉發和代理
293
+
294
+ ```csharp
295
+ services.AddHttpContextAccessor();
296
+ app.UseEnableBuffering();
297
+
298
+ [HttpGet]
299
+ public Task<IActionResult?> ForwardToWebSite()
300
+ {
301
+ return httpContextAccessor.HttpContext.ForwardAsResultAsync("https://github.com");
302
+ }
303
+ ```
304
+
305
+ ### 19.2.16 WebService 介面請求(SOAP)
306
+
307
+ SOAP 1.1 需設定 `SOAPAction` 標頭並使用 `text/xml`;SOAP 1.2 使用 `application/soap+xml`。
308
+
309
+ ### 19.2.17 HTTP 請求斷言(Assert)
310
+
311
+ ```csharp
312
+ HttpRequestBuilder.Get("https://furion.net")
313
+ .EnableAssertions()
314
+ .Asserts(ast => ast.StatusCode(200)
315
+ .HeaderExists("encoding")
316
+ .DurationUnder(5000));
317
+ ```
318
+
319
+ 內建斷言方法:`StatusCode`、`StatusCodeIn`、`IsSuccessStatusCode`、`ContentContains`、`HeaderExists`、`HeaderEquals`、`HeaderContains`、`DurationUnder`、`AddAssertion`。
320
+
321
+ ### 19.2.18 JSON 回應反序列化包裝器
322
+
323
+ ```csharp
324
+ services.AddHttpClient(string.Empty)
325
+ .ConfigureOptions(options =>
326
+ {
327
+ options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data));
328
+ options.UseJsonResponseWrapping = true; // 全域啟用
329
+ });
330
+
331
+ // 使用時無需指定 ApiResult<T>
332
+ var content = await httpRemoteService.SendAsAsync<string>(
333
+ HttpRequestBuilder.Get("https://example.com"));
334
+ ```
335
+
336
+ ### 19.2.19 服務發現(ServiceDiscovery)
337
+
338
+ ```csharp
339
+ services.AddServiceDiscovery();
340
+ services.AddHttpRemote()
341
+ .ConfigureHttpClientDefaults(b => b.AddServiceDiscovery());
342
+ ```
343
+
344
+ 設定檔:
345
+
346
+ ```json
347
+ {
348
+ "Services": {
349
+ "furion": { "https": ["localhost:5001", "furion.net"] }
350
+ }
351
+ }
352
+ ```
353
+
354
+ ### 19.2.20 HttpRemoteResult\<TResult\> 回傳型別
355
+
356
+ ```csharp
357
+ var httpResult = await httpRemoteService.GetAsync<string>("https://furion.net/");
358
+
359
+ // 解構表示式
360
+ var (result, response) = await httpRemoteService.GetAsync<string>("https://furion.net/");
361
+ var (result, response, isSuccess, statusCode) = await httpRemoteService.GetAsync<string>("https://furion.net/");
362
+ ```
363
+
364
+ 屬性:`ResponseMessage`、`ContentType`、`StatusCode`、`IsSuccessStatusCode`、`Result`、`RequestDuration`、`Headers`、`RawSetCookies`、`SetCookies` 等。
365
+
366
+ ### 19.2.21 DeepSeek 官方對接 ✨
367
+
368
+ **標準輸出(非串流):**
369
+
370
+ ```csharp
371
+ var result = await httpRemoteService.SendAsync<string>(HttpRequestBuilder
372
+ .Post("https://api.deepseek.com/chat/completions")
373
+ .Profiler(false)
374
+ .AddJwtBearerAuthentication("您的 APIKEY")
375
+ .SetJsonContent("""
376
+ {
377
+ "model": "deepseek-chat",
378
+ "messages": [
379
+ {"role": "system", "content": "你是一個專業的 C# 領域人才。"},
380
+ {"role": "user", "content": "Furion 框架未來前景?"}
381
+ ],
382
+ "stream": false
383
+ }
384
+ """), cancellationToken);
385
+
386
+ dynamic clay = Clay.Parse(result.Result, ClayOptions.Flexible);
387
+ var content = clay.choices[0].message.content;
388
+ ```
389
+
390
+ **串流輸出(SSE):** 將 `stream` 設為 `true`,使用 `HttpRequestBuilder.ServerSentEvents` 接收串流資料。
391
+
392
+ ---
393
+
394
+ ## 19.3 HttpRequestBuilder 請求建構器 ✨
395
+
396
+ > **小貼士**:嫌 `HttpRequestBuilder` 太長?用 `HttpBuilder` 更清爽!
397
+
398
+ ### 建立實例
399
+
400
+ ```csharp
401
+ // 請求謂詞靜態方法(推薦)
402
+ HttpRequestBuilder.Get("https://furion.net/");
403
+ HttpRequestBuilder.Post("https://furion.net/");
404
+ HttpRequestBuilder.Put / Delete / Options / Trace / Patch ...
405
+
406
+ // Create 靜態方法
407
+ HttpRequestBuilder.Create("GET", "https://furion.net/");
408
+ HttpRequestBuilder.Create(HttpMethod.Get, "https://furion.net/");
409
+
410
+ // 從 JSON 設定建立
411
+ HttpRequestBuilder.FromJson("""{ "url": "https://furion.net/", "method": "GET" }""");
412
+ ```
413
+
414
+ ### 方法命名原則
415
+
416
+ - `Set` / `Use` 開頭:設定操作,重複呼叫會覆蓋
417
+ - `With` / `Add` 開頭:疊加操作,重複呼叫會累加
418
+
419
+ ### 常用方法速查
420
+
421
+ | 方法 | 說明 |
422
+ |------|------|
423
+ | `.SetJsonContent(obj)` | 設定 JSON 請求內容 |
424
+ | `.SetFormUrlEncodedContent(obj)` | 設定 URL 編碼表單 |
425
+ | `.SetMultipartContent(multipart => {})` | 設定多部分表單 |
426
+ | `.SetContent(obj, contentType)` | 設定任意請求內容 |
427
+ | `.WithHeader(key, value)` | 新增請求標頭 |
428
+ | `.WithQueryParameter(key, value)` | 新增查詢參數 |
429
+ | `.WithPathParameter(key, value)` | 替換路徑範本參數 |
430
+ | `.WithCookie(key, value)` | 設定 Cookie |
431
+ | `.SetTimeout(ms)` | 設定逾時時間 |
432
+ | `.SetHttpClientName(name)` | 指定 HttpClient 名稱 |
433
+ | `.SetBaseAddress(url)` | 設定請求基底位址 |
434
+ | `.AddJwtBearerAuthentication(token)` | JWT 授權 |
435
+ | `.AddBasicAuthentication(user, pass)` | Basic 授權 |
436
+ | `.Profiler()` | 啟用請求分析工具 |
437
+ | `.DisableCache()` | 停用 HTTP 快取 |
438
+ | `.EnsureSuccessStatusCode()` | 確保請求成功 |
439
+ | `.SuppressExceptions()` | 例外抑制 |
440
+ | `.SimulateBrowser()` | 模擬瀏覽器環境 |
441
+ | `.PerformanceOptimization()` | 啟用效能最佳化 |
442
+ | `.AutoSetHostHeader()` | 自動 Host 標頭 |
443
+ | `.SetReferer(url)` | 設定來源位址(防盜鏈) |
444
+ | `.SetVersion(HttpVersion.Version20)` | 設定 HTTP 版本 |
445
+ | `.EnableAssertions()` | 啟用斷言 |
446
+ | `.JsonResponseWrapping()` | 啟用 JSON 回應包裝器 |
447
+ | `.When(condition, b => {})` | 條件化設定 |
448
+ | `.WithStatusCodeHandler(code, handler)` | 回應狀態碼處理 |
449
+ | `.WithPathSegment(segment)` | 新增路徑片段 |
450
+ | `.SetFragment(fragment)` | 設定片段識別符 |
451
+
452
+ ### 設定路徑參數(範本/設定參數)
453
+
454
+ ```csharp
455
+ // {key} 範本語法
456
+ HttpRequestBuilder.Get("https://furion.net?id={id}&name={name}")
457
+ .WithPathParameter("id", 1)
458
+ .WithPathParameter("name", "Furion");
459
+
460
+ // [[key]] 設定參數語法(從 appsettings.json 讀取)
461
+ HttpRequestBuilder.Get("https://furion.net?id=[[id]]&name=[[name]]");
462
+ ```
463
+
464
+ 設定參數支援:`[[key]]`、`[[key:sub]]`、`[[notfound | bak]]`、`[[notfound || default]]`。
465
+
466
+ ### 回應狀態碼處理程式
467
+
468
+ ```csharp
469
+ HttpRequestBuilder.Get("https://furion.net/")
470
+ .WithStatusCodeHandler(200, async (res, ct) => { /* ... */ })
471
+ .WithStatusCodeHandler("200-299", async (res, ct) => { /* ... */ })
472
+ .WithStatusCodeHandler(">=500", async (res, ct) => { /* ... */ })
473
+ .WithAnyStatusCodeHandler(async (res, ct) => { /* ... */ })
474
+ .WithSuccessStatusCodeHandler(async (res, ct) => { /* ... */ });
475
+ ```
476
+
477
+ ---
478
+
479
+ ## 19.4 HttpMultipartFormDataBuilder 表單建構器
480
+
481
+ 透過 `SetMultipartContent` 方法取得實例:
482
+
483
+ ```csharp
484
+ HttpRequestBuilder.Post("https://furion.net")
485
+ .SetMultipartContent(multipart =>
486
+ {
487
+ multipart.SetBoundary("--------------------") // 設定邊界
488
+ .AddFormItem(1, "id") // 單個表單項
489
+ .AddJson(new { name = "furion" }) // JSON 內容
490
+ .AddObject(new { age = 30 }) // 物件內容
491
+ .AddFileAsStream(@"path\file.jpg", "file") // 本地檔案(串流)
492
+ .AddFileAsByteArray(@"path\file.jpg", "file") // 本地檔案(位元組陣列)
493
+ .AddFileFromRemote("https://...", "files") // 網際網路檔案
494
+ .AddFileFromBase64String("base64...", "files", "test.txt") // Base64 檔案
495
+ .AddFile(MultipartFile.CreateFromPath(@"path\file.jpg", "files")) // MultipartFile
496
+ .AddFile(formFile) // IFormFile
497
+ .AddStream(stream, "data") // 串流
498
+ .AddByteArray(bytes, "data") // 位元組陣列
499
+ .SetFormNameTransformer(FormNamingPolicy.CamelCase); // 表單名稱策略
500
+ });
501
+ ```
502
+
503
+ ---
504
+
505
+ ## 19.5 HTTP 宣告式請求 ✨
506
+
507
+ ### 介面定義與註冊
508
+
509
+ ```csharp
510
+ public interface IHttpService : IHttpDeclarative
511
+ {
512
+ [Get("https://furion.net/")]
513
+ Task<string> GetMethodAsync();
514
+
515
+ [Post("https://furion.net/")]
516
+ Task<string> PostMethodAsync();
517
+ }
518
+
519
+ services.AddHttpRemote(builder =>
520
+ {
521
+ builder.AddHttpDeclarativesFromAssemblies([Assembly.GetEntryAssembly()]);
522
+ });
523
+ ```
524
+
525
+ ### 常用宣告式特性速查
526
+
527
+ | 特性 | 說明 | 作用範圍 |
528
+ |------|------|----------|
529
+ | `[Get]` / `[Post]` / `[Put]` / `[Delete]` ... | HTTP 請求方法 | 方法 |
530
+ | `[Query(name, value)]` | 查詢參數 | 介面/方法/參數 |
531
+ | `[Header(name, value)]` | 請求標頭 | 介面/方法/參數 |
532
+ | `[Cookie(name, value)]` | Cookie | 介面/方法/參數 |
533
+ | `[Path(name, value)]` | 路徑參數 | 介面/方法 |
534
+ | `[PathSegment(segment)]` | 路徑片段 | 介面/方法/參數 |
535
+ | `[Body(contentType)]` | 請求內容 | 參數 |
536
+ | `[JsonBody]` / `[FormUrlEncodedBody]` | 快速 Body 標記 | 參數 |
537
+ | `[Multipart]` | 多部分表單項 | 參數 |
538
+ | `[MultipartObject]` | 物件作為表單項 | 參數 |
539
+ | `[MultipartForm(boundary)]` | 多部分表單設定 | 方法 |
540
+ | `[HttpClientName(name)]` | HttpClient 名稱 | 介面/方法 |
541
+ | `[BaseAddress(url)]` | 請求基底位址 | 介面/方法 |
542
+ | `[Timeout(ms)]` | 逾時時間 | 介面/方法 |
543
+ | `[TraceIdentifier(id)]` | 追蹤識別碼 | 介面/方法 |
544
+ | `[Profiler]` | 請求分析工具 | 介面/方法 |
545
+ | `[DisableCache]` | 停用快取 | 介面/方法 |
546
+ | `[EnsureSuccessStatusCode]` | 確保成功 | 介面/方法 |
547
+ | `[SimulateBrowser]` | 模擬瀏覽器 | 介面/方法 |
548
+ | `[PerformanceOptimization]` | 效能最佳化 | 介面/方法 |
549
+ | `[AutoSetHostHeader]` | 自動 Host | 介面/方法 |
550
+ | `[AcceptLanguage(lang)]` | 語言偏好 | 介面/方法 |
551
+ | `[Referer(url)]` | 來源位址 | 介面/方法 |
552
+ | `[HttpVersion(ver)]` | HTTP 版本 | 介面/方法 |
553
+ | `[SuppressExceptions]` | 例外抑制 | 介面/方法 |
554
+ | `[Property(name, value)]` | HttpRequestMessage 屬性 | 介面/方法/參數 |
555
+ | `[RequestEventHandler(type)]` | 請求處理程式 | 介面/方法 |
556
+ | `[JsonResponseWrapping]` | JSON 回應包裝器 | 介面/方法 |
557
+ | `[Authentication]` (自訂) | 授權 | 介面/方法 |
558
+ | `[Required]` / `[MinLength]` 等 | 參數驗證 | 參數 |
559
+
560
+ ### 凍結參數型別
561
+
562
+ 在宣告式介面方法中,以下型別為凍結參數,專門服務於特定操作:
563
+
564
+ | 型別 | 說明 |
565
+ |------|------|
566
+ | `Action<HttpRequestBuilder>` | 額外設定建構器 |
567
+ | `Action<HttpMultipartFormDataBuilder>` | 額外設定多部分表單 |
568
+ | `Action<HttpRequestMessage>` | 額外設定 HttpRequestMessage |
569
+ | `HttpCompletionOption` | 回應讀取方式 |
570
+ | `CancellationToken` | 取消操作 |
571
+
572
+ ```csharp
573
+ public interface IHttpService : IHttpDeclarative
574
+ {
575
+ [Post("https://furion.net/")]
576
+ Task<string> PostAsync([Query] int id, [Body("application/json")] object body,
577
+ Action<HttpRequestBuilder>? configure = null,
578
+ CancellationToken cancellationToken = default);
579
+ }
580
+
581
+ // 呼叫時
582
+ await httpService.PostAsync(1, new { name = "furion" },
583
+ builder => builder.AddJwtBearerAuthentication("token"),
584
+ cancellationToken);
585
+ ```
586
+
587
+ ### 自訂 HTTP 宣告提取器
588
+
589
+ ```csharp
590
+ // 1. 定義特性
591
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)]
592
+ public sealed class AcceptAttribute : Attribute
593
+ {
594
+ public AcceptAttribute(string accept) => Accept = accept;
595
+ public string Accept { get; set; }
596
+ }
597
+
598
+ // 2. 實作提取器
599
+ public sealed class AcceptDeclarativeExtractor : IHttpDeclarativeExtractor
600
+ {
601
+ public void Extract(HttpRequestBuilder httpRequestBuilder, HttpDeclarativeExtractorContext context)
602
+ {
603
+ if (!context.IsMethodDefined<AcceptAttribute>(out var attr, true)) return;
604
+ httpRequestBuilder.WithHeader("Accept", attr.Accept, replace: true);
605
+ }
606
+ }
607
+
608
+ // 3. 註冊
609
+ services.AddHttpRemote(builder =>
610
+ {
611
+ builder.AddHttpDeclarativeExtractors(() => [new AcceptDeclarativeExtractor()]);
612
+ });
613
+
614
+ // 4. 使用
615
+ [Accept("text/html")]
616
+ public interface IHttpService : IHttpDeclarative
617
+ {
618
+ [Get("https://furion.net/")]
619
+ Task<string> GetStringAsync();
620
+ }
621
+ ```