@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,328 @@
|
|
|
1
|
+
# 5.5 中介軟體(Middleware)
|
|
2
|
+
|
|
3
|
+
## 5.5.1 關於中介軟體
|
|
4
|
+
|
|
5
|
+
中介軟體是一種裝配到應用管線以處理請求和回應的軟體。每個組件:
|
|
6
|
+
|
|
7
|
+
- 選擇是否將請求傳遞到管線中的下一個組件
|
|
8
|
+
- 可在管線中的下一個組件前後執行工作
|
|
9
|
+
|
|
10
|
+
請求委派用於產生請求管線,請求委派處理每個 HTTP 請求。
|
|
11
|
+
|
|
12
|
+
> 一句話總結:中介軟體是比篩選器更底層、更上游的面向切面技術,其效能最高,可處理的應用範圍遠比過濾器廣,如實現閘道、URL 轉發、限流等等。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 5.5.2 常見中介軟體
|
|
17
|
+
|
|
18
|
+
### 5.5.2.1 所有請求回傳同一個結果
|
|
19
|
+
|
|
20
|
+
```csharp
|
|
21
|
+
app.Run(async context =>
|
|
22
|
+
{
|
|
23
|
+
await context.Response.WriteAsync("Hello world!");
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 5.5.2.2 攔截所有請求(可多個)
|
|
28
|
+
|
|
29
|
+
```csharp
|
|
30
|
+
app.Use(async (context, next) =>
|
|
31
|
+
{
|
|
32
|
+
// 比如設定統一標頭
|
|
33
|
+
context.Response.Headers["framework"] = "Furion";
|
|
34
|
+
|
|
35
|
+
// 執行下一個中介軟體
|
|
36
|
+
await next.Invoke();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// 多個
|
|
40
|
+
app.Use(...);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 5.5.2.3 特定路由中介軟體(可多個)
|
|
44
|
+
|
|
45
|
+
```csharp
|
|
46
|
+
app.Map("/hello", app => {
|
|
47
|
+
app.Run(async context =>
|
|
48
|
+
{
|
|
49
|
+
await context.Response.WriteAsync("Map Test 1");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
app.Map("/hello/say", app => {
|
|
54
|
+
// ....
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 5.5.2.4 巢狀路由中介軟體(可多個)
|
|
59
|
+
|
|
60
|
+
```csharp
|
|
61
|
+
app.Map("/level1", level1App => {
|
|
62
|
+
level1App.Map("/level2a", level2AApp => {
|
|
63
|
+
// "/level1/level2a" processing
|
|
64
|
+
});
|
|
65
|
+
level1App.Map("/level2b", level2BApp => {
|
|
66
|
+
// "/level1/level2b" processing
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 5.5.3 自訂中介軟體
|
|
74
|
+
|
|
75
|
+
自訂中介軟體有多種方式,最簡單的是透過 `app.Use` 方式,另外還支援獨立類別定義方式。
|
|
76
|
+
|
|
77
|
+
### 5.5.3.1 app.Use 方式(不推薦)
|
|
78
|
+
|
|
79
|
+
```csharp
|
|
80
|
+
app.Use(async (context, next) =>
|
|
81
|
+
{
|
|
82
|
+
var cultureQuery = context.Request.Query["culture"];
|
|
83
|
+
if (!string.IsNullOrWhiteSpace(cultureQuery))
|
|
84
|
+
{
|
|
85
|
+
var culture = new CultureInfo(cultureQuery);
|
|
86
|
+
CultureInfo.CurrentCulture = culture;
|
|
87
|
+
CultureInfo.CurrentUICulture = culture;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await next(context);
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 5.5.3.2 獨立類別方式(推薦)
|
|
95
|
+
|
|
96
|
+
獨立類別的方式是目前最為推薦的方式,擴充性強,維護性高。
|
|
97
|
+
|
|
98
|
+
**定義中介軟體(建議以 `Middleware` 結尾):**
|
|
99
|
+
|
|
100
|
+
```csharp
|
|
101
|
+
using System.Globalization;
|
|
102
|
+
|
|
103
|
+
namespace Middleware.Example;
|
|
104
|
+
|
|
105
|
+
public class RequestCultureMiddleware
|
|
106
|
+
{
|
|
107
|
+
private readonly RequestDelegate _next;
|
|
108
|
+
|
|
109
|
+
public RequestCultureMiddleware(RequestDelegate next)
|
|
110
|
+
{
|
|
111
|
+
_next = next;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public async Task InvokeAsync(HttpContext context)
|
|
115
|
+
{
|
|
116
|
+
var cultureQuery = context.Request.Query["culture"];
|
|
117
|
+
if (!string.IsNullOrWhiteSpace(cultureQuery))
|
|
118
|
+
{
|
|
119
|
+
var culture = new CultureInfo(cultureQuery);
|
|
120
|
+
CultureInfo.CurrentCulture = culture;
|
|
121
|
+
CultureInfo.CurrentUICulture = culture;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 呼叫下一個中介軟體
|
|
125
|
+
await _next(context);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**新增中介軟體擴充類別(建議以 `Use` 開頭):**
|
|
131
|
+
|
|
132
|
+
```csharp
|
|
133
|
+
public static class RequestCultureMiddlewareExtensions
|
|
134
|
+
{
|
|
135
|
+
public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
|
|
136
|
+
{
|
|
137
|
+
return builder.UseMiddleware<RequestCultureMiddleware>();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**在 Startup.cs 中使用:**
|
|
143
|
+
|
|
144
|
+
```csharp
|
|
145
|
+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
|
146
|
+
{
|
|
147
|
+
// ... 其他中介軟體
|
|
148
|
+
app.UseRequestCulture();
|
|
149
|
+
// ... 其他中介軟體
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 5.5.3.3 設定更多參數
|
|
154
|
+
|
|
155
|
+
預設情況下,自訂獨立類別中介軟體建構函式只有一個 `RequestDelegate` 參數,除此之外還可以注入服務介面/類別,另外還支援傳入任何其他類型。
|
|
156
|
+
|
|
157
|
+
**服務類型參數:**
|
|
158
|
+
|
|
159
|
+
```csharp
|
|
160
|
+
public class RequestCultureMiddleware
|
|
161
|
+
{
|
|
162
|
+
private readonly RequestDelegate _next;
|
|
163
|
+
private readonly ILogger<RequestCultureMiddleware> _logger;
|
|
164
|
+
|
|
165
|
+
public RequestCultureMiddleware(RequestDelegate next, ILogger<RequestCultureMiddleware> logger)
|
|
166
|
+
{
|
|
167
|
+
_next = next;
|
|
168
|
+
_logger = logger;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public async Task InvokeAsync(HttpContext context)
|
|
172
|
+
{
|
|
173
|
+
_logger.LogInformation("...");
|
|
174
|
+
await _next(context);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**非服務類型參數**(必須宣告在服務參數之後):
|
|
180
|
+
|
|
181
|
+
```csharp
|
|
182
|
+
public class RequestCultureMiddleware
|
|
183
|
+
{
|
|
184
|
+
private readonly RequestDelegate _next;
|
|
185
|
+
private readonly ILogger<RequestCultureMiddleware> _logger;
|
|
186
|
+
|
|
187
|
+
public RequestCultureMiddleware(RequestDelegate next,
|
|
188
|
+
ILogger<RequestCultureMiddleware> logger,
|
|
189
|
+
int age,
|
|
190
|
+
string name)
|
|
191
|
+
{
|
|
192
|
+
_next = next;
|
|
193
|
+
_logger = logger;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public async Task InvokeAsync(HttpContext context)
|
|
197
|
+
{
|
|
198
|
+
_logger.LogInformation("...");
|
|
199
|
+
await _next(context);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
修改中介軟體擴充類別:
|
|
205
|
+
|
|
206
|
+
```csharp
|
|
207
|
+
public static class RequestCultureMiddlewareExtensions
|
|
208
|
+
{
|
|
209
|
+
public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder, int age, string name)
|
|
210
|
+
{
|
|
211
|
+
return builder.UseMiddleware<RequestCultureMiddleware>(new object[] { age, name });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
使用:
|
|
217
|
+
|
|
218
|
+
```csharp
|
|
219
|
+
app.UseRequestCulture(30, "百小僧");
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 5.5.4 中介軟體順序
|
|
225
|
+
|
|
226
|
+
中介軟體是有執行順序的,而且是先註冊的先執行,無法透過其他方式更改。
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 5.5.5 相依性注入/解析服務
|
|
231
|
+
|
|
232
|
+
中介軟體有兩種方式注入服務:一種是透過建構函式注入,一種是透過 `httpContext.RequestServices` 方式解析。
|
|
233
|
+
|
|
234
|
+
### 5.5.5.1 建構函式方式
|
|
235
|
+
|
|
236
|
+
```csharp
|
|
237
|
+
public class RequestCultureMiddleware
|
|
238
|
+
{
|
|
239
|
+
private readonly RequestDelegate _next;
|
|
240
|
+
private readonly ILogger<RequestCultureMiddleware> _logger;
|
|
241
|
+
|
|
242
|
+
public RequestCultureMiddleware(RequestDelegate next,
|
|
243
|
+
ILogger<RequestCultureMiddleware> logger,
|
|
244
|
+
IHostEnvironment hostEnvironment)
|
|
245
|
+
{
|
|
246
|
+
_next = next;
|
|
247
|
+
_logger = logger;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public async Task InvokeAsync(HttpContext context)
|
|
251
|
+
{
|
|
252
|
+
await _next(context);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 5.5.5.2 httpContext.RequestServices 方式
|
|
258
|
+
|
|
259
|
+
`HttpContext` 提供了 `RequestServices` 屬性方便解析服務:
|
|
260
|
+
|
|
261
|
+
```csharp
|
|
262
|
+
public class RequestCultureMiddleware
|
|
263
|
+
{
|
|
264
|
+
private readonly RequestDelegate _next;
|
|
265
|
+
private readonly ILogger<RequestCultureMiddleware> _logger;
|
|
266
|
+
|
|
267
|
+
public RequestCultureMiddleware(RequestDelegate next,
|
|
268
|
+
ILogger<RequestCultureMiddleware> logger,
|
|
269
|
+
IHostEnvironment hostEnvironment)
|
|
270
|
+
{
|
|
271
|
+
_next = next;
|
|
272
|
+
_logger = logger;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public async Task InvokeAsync(HttpContext context)
|
|
276
|
+
{
|
|
277
|
+
// 透過 context.RequestServices 解析
|
|
278
|
+
var repository = context.RequestServices.GetService<IRepository>();
|
|
279
|
+
|
|
280
|
+
await _next(context);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## 5.5.6 刪除特定的 HTTP 回應標頭
|
|
288
|
+
|
|
289
|
+
預設情況下,Furion 框架會輸出當前的執行環境和 Furion 版本資訊,如無需顯示,可在 `Startup.cs` 中移除:
|
|
290
|
+
|
|
291
|
+
```csharp
|
|
292
|
+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
|
293
|
+
{
|
|
294
|
+
if (env.IsDevelopment())
|
|
295
|
+
{
|
|
296
|
+
app.UseDeveloperExceptionPage();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
app.Use(async (context, next) =>
|
|
300
|
+
{
|
|
301
|
+
context.Response.Headers.Remove("furion");
|
|
302
|
+
context.Response.Headers.Remove("environment");
|
|
303
|
+
|
|
304
|
+
await next();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// .... 其他程式碼
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 5.5.7 常見問題
|
|
314
|
+
|
|
315
|
+
有時候我們需要在中介軟體中取得終點路由的特性或其他資訊:
|
|
316
|
+
|
|
317
|
+
```csharp
|
|
318
|
+
// 取得終點路由特性
|
|
319
|
+
var endpointFeature = context.Features.Get<IEndpointFeature>();
|
|
320
|
+
|
|
321
|
+
// 取得是否定義了特性
|
|
322
|
+
var attribute = endpointFeature?.Endpoint?.Metadata?.GetMetadata<YourAttribute>();
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
> **注意事項**:要想上面操作有效(不為 `null`),需要滿足以下條件:
|
|
326
|
+
>
|
|
327
|
+
> - 啟用端點路由 `AddControllers()` 而不是 `AddMvc()`
|
|
328
|
+
> - 在 `UseRouting()` 和 `UseEndpoints()` 之間呼叫你的中介軟體
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# 5.6 Vue/React/Angular 介面代理
|
|
2
|
+
|
|
3
|
+
> **影片教學**:https://www.bilibili.com/video/BV1EW4y1C71D
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 5.6.1 歷史背景
|
|
8
|
+
|
|
9
|
+
在現在主流的 Web 專案開發中,越來越多的開發者選擇使用 Vue/React/Angular 三大框架進行開發,這三大框架和傳統開發模式最大的不同是前者採用前後端分離的方式。
|
|
10
|
+
|
|
11
|
+
在前後端分離的模式中,前後端程式設計師各司其職,後端程式負責編寫介面(API),前端程式設計師負責編寫客戶端請求後端介面(API)並進行資料繫結。
|
|
12
|
+
|
|
13
|
+
但這裡暴露出了一個工作效率極低且易出錯的問題:前端程式需要將後端數百個甚至上千個介面進行一一對應編寫,大多都是採用 `$.ajax` 或 `axios` 的方式。一旦後端介面參數或回傳值發生改變,前端程式設計師需要一一進行勘正。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 5.6.2 如何解決?
|
|
18
|
+
|
|
19
|
+
Furion 框架編寫的所有後端介面都會產生規範化的 `swagger.json` 檔案,使用該檔案可以在 https://editor.swagger.io 產生任何支援標準 Swagger 的介面或客戶端程式碼。
|
|
20
|
+
|
|
21
|
+
自此,前端程式設計師再也無需自己手寫 `$.ajax` 和 `axios` 程式碼,這部分程式碼全部自動產生。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 5.6.3 產生客戶端請求程式碼
|
|
26
|
+
|
|
27
|
+
> **關於 TypeScript 和 JavaScript**:以下教學僅適用於 Vue/React/Angular 的 TypeScript 類型專案,暫不支援 JavaScript。建議使用 TypeScript 進行編寫。
|
|
28
|
+
|
|
29
|
+
### 5.6.3.1 產生客戶端程式碼
|
|
30
|
+
|
|
31
|
+
1. 開啟規範化文件(Swagger)首頁,並點擊頂部 `/swagger/xxxx/swagger.json` 到新視窗開啟
|
|
32
|
+
2. 全選並複製全部內容
|
|
33
|
+
3. 開啟 https://editor.swagger.io 官網並貼上進去
|
|
34
|
+
|
|
35
|
+
> **無法連網**:Furion 也提供了 `Swagger-Editor.rar` 離線包,可直接下載解壓縮並雙擊 `index.html` 即可。
|
|
36
|
+
|
|
37
|
+
4. 最後點擊頂部的 **Generate Client** 選擇對應的語言/框架進行產生即可
|
|
38
|
+
|
|
39
|
+
### 5.6.3.2 Vue/React 設定
|
|
40
|
+
|
|
41
|
+
1. 點擊 **Generate Client** 頂部選單並選擇 `typescript-axios` 進行下載
|
|
42
|
+
2. 下載成功之後拷貝產生的目錄和檔案
|
|
43
|
+
3. 在你的 Vue 或 React 專案的 `src` 目錄下建立 `api-services` 目錄並將剛剛複製的目錄檔案放在裡面
|
|
44
|
+
4. 透過 npm 或 yarn 安裝 `axios` 套件:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# npm 方式
|
|
48
|
+
npm i axios@0.21.4
|
|
49
|
+
|
|
50
|
+
# yarn 方式
|
|
51
|
+
yarn add axios@0.21.4
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> ⚠️ **axios 版本說明**:注意 axios 版本必須是 `0.21.4` 版本,如果安裝其他版本可能會出現無法編譯的情況。
|
|
55
|
+
|
|
56
|
+
5. 下載 Furion 提供的 Vue/React 工具庫 `axios-utils.ts` 並拷貝到和 `api-services` 同級目錄下
|
|
57
|
+
|
|
58
|
+
> **Vue3 專案不能編譯問題**:如果在 Vue3 專案中無法編譯通過,則需要修改根目錄下的 `tsconfig.app.json`、`tsconfig.vite-config.json` 和 `tsconfig.vitest.json` 檔案並新增下列設定:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
"compilerOptions": {
|
|
62
|
+
"importsNotUsedAsValues": "remove",
|
|
63
|
+
"preserveValueImports": false
|
|
64
|
+
// TypeScript 5.0+ 使用以下設定代替上面兩行
|
|
65
|
+
// "verbatimModuleSyntax": false
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 5.6.3.3 Angular 設定
|
|
70
|
+
|
|
71
|
+
1. 點擊 **Generate Client** 頂部選單並選擇 `typescript-angular` 進行下載
|
|
72
|
+
2. 下載成功之後拷貝產生的目錄和檔案
|
|
73
|
+
3. 在你的 Angular 專案的 `src` 目錄下建立 `api-services` 目錄並將剛剛複製的目錄檔案放在裡面
|
|
74
|
+
4. 下載 Furion 提供的 Angular 工具庫 `angular-utils.ts` 並拷貝到和 `api-services` 同級目錄下
|
|
75
|
+
|
|
76
|
+
> **Angular 專案不能編譯問題**:如果無法編譯通過,需要修改 `api-services/encoder.ts` 檔案,在 `encodeKey` 和 `encodeValue` 前新增 `override`:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// api-services/encoder.ts
|
|
80
|
+
export class CustomHttpUrlEncodingCodec extends HttpUrlEncodingCodec {
|
|
81
|
+
override encodeKey(k: string): string {
|
|
82
|
+
k = super.encodeKey(k);
|
|
83
|
+
return k.replace(/\+/gi, "%2B");
|
|
84
|
+
}
|
|
85
|
+
override encodeValue(v: string): string {
|
|
86
|
+
v = super.encodeValue(v);
|
|
87
|
+
return v.replace(/\+/gi, "%2B");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
5. 在 `src/app/app.module.ts` 中註冊 `ServeModule`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { NgModule } from "@angular/core";
|
|
96
|
+
import { BrowserModule } from "@angular/platform-browser";
|
|
97
|
+
import { AppRoutingModule } from "./app-routing.module";
|
|
98
|
+
import { AppComponent } from "./app.component";
|
|
99
|
+
import { ServeModule } from "src/angular-utils";
|
|
100
|
+
|
|
101
|
+
@NgModule({
|
|
102
|
+
declarations: [AppComponent],
|
|
103
|
+
imports: [
|
|
104
|
+
BrowserModule,
|
|
105
|
+
AppRoutingModule,
|
|
106
|
+
ServeModule, // 註冊代理服務模組
|
|
107
|
+
],
|
|
108
|
+
providers: [],
|
|
109
|
+
bootstrap: [AppComponent],
|
|
110
|
+
})
|
|
111
|
+
export class AppModule {}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 5.6.4 初始設定
|
|
117
|
+
|
|
118
|
+
完成上面步驟之後還需要最後一步:修改伺服端(後端)介面(API)位址。
|
|
119
|
+
|
|
120
|
+
### 5.6.4.1 Vue/React 設定
|
|
121
|
+
|
|
122
|
+
編輯 `axios-utils.ts` 檔案,將 `serveConfig` 修改為對應的後端位址:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import globalAxios, { AxiosInstance } from "axios";
|
|
126
|
+
import { Configuration } from "./api-services";
|
|
127
|
+
import { BaseAPI, BASE_PATH } from "./api-services/base";
|
|
128
|
+
|
|
129
|
+
export const serveConfig = new Configuration({
|
|
130
|
+
basePath:
|
|
131
|
+
process.env.NODE_ENV !== "production"
|
|
132
|
+
? "https://localhost:44342" // 開發環境伺服器介面位址
|
|
133
|
+
: "https://furion.net", // 生產環境伺服器介面位址
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 5.6.4.2 Angular 設定
|
|
138
|
+
|
|
139
|
+
編輯 `angular-utils.ts` 檔案,將 `serveConfig` 修改為對應的後端位址:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { environment } from "./environments/environment";
|
|
143
|
+
|
|
144
|
+
export const serveConfig = new Configuration({
|
|
145
|
+
basePath: !environment.production
|
|
146
|
+
? "https://localhost:44316" // 開發環境伺服器介面位址
|
|
147
|
+
: "https://furion.net", // 生產環境伺服器介面位址
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 5.6.5 基本使用
|
|
154
|
+
|
|
155
|
+
### 5.6.5.1 Vue/React 中使用
|
|
156
|
+
|
|
157
|
+
在 Vue/React 中使用有兩種方式:Promise 和 async/await,推薦使用後者。
|
|
158
|
+
|
|
159
|
+
**Promise 方式:**
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { getAPI } from "../axios-utils";
|
|
163
|
+
|
|
164
|
+
getAPI(SystemAPI)
|
|
165
|
+
.apiGetXXXX()
|
|
166
|
+
.then((res) => {
|
|
167
|
+
var data = res.data.data!;
|
|
168
|
+
})
|
|
169
|
+
.catch((err) => {
|
|
170
|
+
console.log(err);
|
|
171
|
+
})
|
|
172
|
+
.finally(() => {
|
|
173
|
+
console.log("api request completed.");
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**async/await 方式:**
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { getAPI, feature } from "../axios-utils";
|
|
181
|
+
|
|
182
|
+
const [err, res] = await feature(getAPI(SystemAPI).apiGetXXX());
|
|
183
|
+
|
|
184
|
+
if (err) {
|
|
185
|
+
console.log(err);
|
|
186
|
+
} else {
|
|
187
|
+
var data = res.data.data!;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log("api request completed.");
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
> **關於檔案串流下載**:對於檔案串流下載可能存在下載檔案過大的情況,需要新增 `options` 參數 `responseType: "blob"`:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
getAPI(SystemAPI, { responseType: "blob" }).apiGetXXX();
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 5.6.5.2 Angular 中使用
|
|
200
|
+
|
|
201
|
+
在 Angular 專案中,透過建構函式注入對應的服務即可:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { Component } from "@angular/core";
|
|
205
|
+
import { PersonService } from "src/api-services";
|
|
206
|
+
|
|
207
|
+
@Component({
|
|
208
|
+
selector: "app-root",
|
|
209
|
+
templateUrl: "./app.component.html",
|
|
210
|
+
styleUrls: ["./app.component.css"],
|
|
211
|
+
})
|
|
212
|
+
export class AppComponent {
|
|
213
|
+
title = "angulars";
|
|
214
|
+
|
|
215
|
+
constructor(private personService: PersonService) {}
|
|
216
|
+
|
|
217
|
+
ngOnInit(): void {
|
|
218
|
+
this.personService.apiPersonAllGet().subscribe({
|
|
219
|
+
next: (res) => {
|
|
220
|
+
console.log(res);
|
|
221
|
+
},
|
|
222
|
+
error: (err) => {
|
|
223
|
+
// 請求失敗
|
|
224
|
+
},
|
|
225
|
+
complete: () => {
|
|
226
|
+
// 請求完成
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 5.6.6 重新產生程式碼
|
|
236
|
+
|
|
237
|
+
如果後端介面(API)發生改變,只需要刪除 `api-services` 下所有目錄檔案並重新產生複製進去即可。
|
|
238
|
+
|
|
239
|
+
> **關於 Angular 專案**:可以保留 `api-services/encoder.ts` 檔案並刪除其他目錄檔案,新產生的目錄檔案無需複製 `encoder.ts`,這樣可以避免每次修改。
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 5.6.7 Swagger 多分組產生
|
|
244
|
+
|
|
245
|
+
在很多大型系統中,為了方便對介面進行歸類,往往使用了 Swagger 多分組功能,這樣會使系統的介面散落在多個 `swagger.json` 中。
|
|
246
|
+
|
|
247
|
+
這個時候,需要在後端規範化文件中啟用多分組設定:
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"SpecificationDocumentSettings": {
|
|
252
|
+
"EnableAllGroups": true
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
啟用設定之後在 Swagger 導覽列頂部下拉分組將出現 **All Groups** 選項,使用這個 All Groups 的 `swagger.json` 進行產生。
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 5.6.8 自訂產生前端方法名
|
|
262
|
+
|
|
263
|
+
> **版本說明**:僅限 Furion 4.1.7+ 版本使用。
|
|
264
|
+
|
|
265
|
+
透過 `[OperationId]` 來設定產生的前端名稱:
|
|
266
|
+
|
|
267
|
+
```csharp
|
|
268
|
+
using Furion.SpecificationDocument;
|
|
269
|
+
|
|
270
|
+
public class PersonDto
|
|
271
|
+
{
|
|
272
|
+
[OperationId(nameof(TestMethod))]
|
|
273
|
+
public string TestMethod()
|
|
274
|
+
{
|
|
275
|
+
// ...
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 5.6.9 框架客戶端工具庫介紹
|
|
283
|
+
|
|
284
|
+
`axios-utils.ts` 和 `angular-utils.ts` 是 Furion 框架專門針對 Furion 開發的 WebAPI 專案編寫的客戶端代理庫,在這個代理庫中已經處理了跨域、授權、自動重新整理 Token 以及解密客戶端 JWT Token 問題。
|
|
285
|
+
|
|
286
|
+
同時提供了非常方便的 `feature` 方法,可將非同步方法進行同步化處理。
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 5.6.10 處理 GET 請求 Url 位址陣列參數
|
|
291
|
+
|
|
292
|
+
預設情況下,axios 會將陣列類型的 Url 參數透過 `,` 逗號傳遞(如 `https://furion.net?arr=1,2,3`),但後端通常支援的格式為 `https://furion.net?arr[0]=1&arr[1]=2&arr[2]=3`。
|
|
293
|
+
|
|
294
|
+
透過安裝 `qs` 套件並設定 `paramsSerializer` 即可:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
npm install qs
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// 頂部位置
|
|
302
|
+
import qs from 'qs';
|
|
303
|
+
|
|
304
|
+
// 設定 axios
|
|
305
|
+
axiosInstance.defaults.timeout = 1000 * 60 * 10; // 設定逾時,預設 10 分鐘
|
|
306
|
+
axiosInstance.defaults.paramsSerializer = (params) => qs.stringify(params);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 5.6.11 無法連線外網/內網情況/離線包
|
|
312
|
+
|
|
313
|
+
在一些比較注重程式碼安全的組織或公司中,可能不能連線外網進行產生,這個時候只需要下載 https://github.com/swagger-api/swagger-editor 程式碼在本地部署即可。
|
|
314
|
+
|
|
315
|
+
Furion 官網也提供了離線包下載:https://gitee.com/dotnetchina/Furion/blob/v4/clients/Swagger-Editor.rar
|
|
316
|
+
|
|
317
|
+
下載離線包後直接雙擊 `index.html` 啟動即可。
|