@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.
- package/dist/index.js +83 -88
- package/knowledge/Furion_Teaching_Manual/04-1-/351/205/215/347/275/256.md +442 -0
- package/knowledge/Furion_Teaching_Manual/04-2-/351/201/270/351/240/205.md +363 -0
- package/knowledge/Furion_Teaching_Manual/05-1-/345/213/225/346/205/213WebAPI.md +825 -0
- package/knowledge/Furion_Teaching_Manual/05-2-HttpContext.md +217 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/05-5-/344/270/255/344/273/213/350/273/237/351/253/224Middleware.md +328 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/06-2/347/254/254/344/270/211/346/226/271API_Scalar.md +91 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/10-1-SqlSugar/346/225/264/345/220/210.md +336 -0
- 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
- package/knowledge/Furion_Teaching_Manual/12-furion-dependency-injection.md +408 -0
- 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
- package/knowledge/Furion_Teaching_Manual/14-/345/210/206/345/270/203/345/274/217/347/274/223/345/255/230.md +311 -0
- package/knowledge/Furion_Teaching_Manual/15-/345/256/211/345/205/250/351/211/264/346/235/203.md +832 -0
- 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
- package/knowledge/Furion_Teaching_Manual/18-/346/227/245/350/252/214/350/250/230/351/214/204.md +639 -0
- 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
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/20-/350/263/207/346/226/231/345/212/240/350/247/243/345/257/206.md +286 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/22-/344/272/213/344/273/266/345/214/257/346/265/201/346/216/222EventBus.md +448 -0
- package/knowledge/Furion_Teaching_Manual/23-JSON/345/272/217/345/210/227/345/214/226.md +340 -0
- package/knowledge/Furion_Teaching_Manual/24-/345/215/263/346/231/202/351/200/232/350/250/212SignalR.md +247 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/26-2-Cron/350/241/250/351/201/224/345/274/217.md +203 -0
- package/knowledge/Furion_Teaching_Manual/26-3-/344/273/273/345/213/231/344/275/207/345/210/227TaskQueue.md +215 -0
- package/knowledge/Furion_Teaching_Manual/27-/345/210/206/346/225/243/345/274/217ID/347/224/237/346/210/220.md +86 -0
- package/knowledge/Furion_Teaching_Manual/28-/346/250/241/347/265/204/345/214/226/351/226/213/347/231/274.md +68 -0
- package/knowledge/Furion_Teaching_Manual/29-/346/265/201/350/256/212/347/211/251/344/273/266Clay.md +313 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/33-IPC/347/250/213/345/272/217/351/200/232/350/250/212.md +98 -0
- package/knowledge/Furion_Teaching_Manual/34-2-Docker/351/203/250/347/275/262.md +313 -0
- package/knowledge/Furion_Teaching_Manual/34-3-Nginx/351/203/250/347/275/262.md +157 -0
- package/knowledge/Furion_Teaching_Manual/34-8-WindowsService/351/203/250/347/275/262.md +112 -0
- 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
- 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
- package/knowledge/Furion_Teaching_Manual/36-3-/345/237/272/346/272/226/346/270/254/350/251/246Benchmarking.md +80 -0
- package/knowledge/attributes.md +153 -0
- package/knowledge/config.md +147 -0
- package/knowledge/entity.md +115 -0
- package/knowledge/event.md +124 -0
- package/knowledge/plugin.md +136 -0
- package/knowledge/service.md +146 -0
- package/knowledge/sqlsugar.md +172 -0
- package/knowledge/vue-typescript.md +338 -0
- 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
|
+
```
|