@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,313 @@
1
+ # 29. 流變物件(Clay / Shapeless)
2
+
3
+ > **版本說明**:僅適用於 Furion 4.9.7+,不支援 .NET 8 以下版本。Furion 已內建;非 Furion 框架可安裝 `Shapeless` 或 `Shapeless.AspNetCore`。
4
+
5
+ ---
6
+
7
+ ## 29.1 概述
8
+
9
+ 流變物件(Clay)是基於 `DynamicObject` 建立的執行階段動態派生物件,提供類似 JavaScript JSON 物件的靈活操作能力,支援動態增刪改查、Linq/Lambda 查詢。
10
+
11
+ **應用場景**:動態資料操作、第三方 API 整合、CMS、工作流表單、微服務資料整合、快速原型開發、資料分析報表、事件驅動架構、設定管理、範本引擎、電商自訂參數等。
12
+
13
+ ---
14
+
15
+ ## 29.2 快速入門
16
+
17
+ ### 建立單一物件(`{}`)
18
+
19
+ ```csharp
20
+ dynamic clay = new Clay(); // 或 new Clay.Object()
21
+ dynamic clay = Clay.EmptyObject();
22
+ dynamic clay = Clay.Parse("{}");
23
+ dynamic clay = Clay.Parse(new { id = 1 });
24
+
25
+ // 便捷初始化
26
+ dynamic clay = new Clay { ["id"] = 1, ["name"] = "Shapeless" };
27
+ ```
28
+
29
+ ### 建立集合或陣列(`[]`)
30
+
31
+ ```csharp
32
+ dynamic clay = new Clay(ClayType.Array); // 或 new Clay.Array()
33
+ dynamic clay = Clay.EmptyArray();
34
+ dynamic clay = Clay.Parse("[]");
35
+ dynamic clay = Clay.Parse(new List<int> { 1, 2, 3 });
36
+ ```
37
+
38
+ ### 基本操作(單一物件)
39
+
40
+ ```csharp
41
+ clay.Id = 1; // 屬性方式新增/設定
42
+ clay["Name"] = "Furion"; // 索引方式
43
+ clay.Author = new { Name = "MonkSoul" }; // 巢狀物件
44
+ clay.Remove("Name"); // 刪除
45
+ clay.Id += 1; // 修改
46
+ Console.WriteLine(clay); // 輸出 JSON
47
+ Console.WriteLine($"{clay:UZC}"); // U=取消Unicode C=camelCase Z=壓縮 P=Pascal
48
+ ```
49
+
50
+ ### 基本操作(集合/陣列)
51
+
52
+ ```csharp
53
+ clay.Add(1); // 追加
54
+ clay.AddRange(new object[] { 2, 3 });
55
+ clay.Insert(0, "first"); // 插入
56
+ clay[0] = "modified"; // 修改
57
+ clay.Remove(0); // 刪除
58
+ clay.Pop(); // 移除末項
59
+ clay.Reverse(); // 反轉
60
+ clay[1..^1]; // 截取(Range)
61
+ ```
62
+
63
+ ### 從多種來源建立
64
+
65
+ ```csharp
66
+ Clay.Parse("""{"id":1}"""); // JSON 字串
67
+ Clay.Parse(new YourModel { Id = 1 }); // 具體型別
68
+ Clay.Parse(new { id = 1 }); // 匿名物件
69
+ Clay.Parse(new Dictionary<string, object> { {"id", 1} });
70
+ Clay.Parse(byteArray); // byte[]
71
+ Clay.Parse(stream); // Stream
72
+ Clay.Parse(jsonElement); // JsonElement
73
+ Clay.ParseFromFile("path.json"); // 檔案
74
+ Clay.Parse("form=data&key=val"); // URL 表單
75
+ new { id = 1 }.ToClay(); // ToClay() 擴充方法
76
+ ```
77
+
78
+ **隱式轉換**(4.9.8.12+):
79
+
80
+ ```csharp
81
+ Clay clay = """{"id":1}"""; // string → Clay
82
+ string json = Clay.Parse("{}"); // Clay → string
83
+ Dictionary<string, object?> dic = clay; // Clay → Dictionary
84
+ ```
85
+
86
+ ### 型別轉換
87
+
88
+ ```csharp
89
+ ClayModel model = clay; // 隱式轉換
90
+ var model = (ClayModel)clay; // 顯式轉換
91
+ var model = clay.As<ClayModel>(); // As<T>() 方法
92
+ var model = clay.As<ClayModel>(new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
93
+ XElement xml = clay; // 轉 XML
94
+ IActionResult result = clay; // 轉 IActionResult
95
+
96
+ // 屬性型別轉換
97
+ var date = clay.date<DateTime>();
98
+ var val = clay.Get<int>("id");
99
+ var val = clay.Get("id", typeof(int));
100
+ ```
101
+
102
+ ### 輸出格式
103
+
104
+ | 方法 | 說明 |
105
+ |------|------|
106
+ | `clay.ToString()` / `Console.WriteLine(clay)` | JSON(預設格式化 + Unicode 編碼) |
107
+ | `clay.ToString("U")` / `$"{clay:U}"` | 取消中文 Unicode |
108
+ | `clay.ToString("Z")` | 壓縮 JSON |
109
+ | `clay.ToString("C")` / `"P"` | camelCase / PascalCase |
110
+ | `clay.ToString("ZUC")` | 組合使用 |
111
+ | `clay.ToJsonString(options)` | 完整 JsonSerializerOptions 控制 |
112
+ | `clay.ToXmlString(settings)` | XML 輸出 |
113
+ | `clay()` | 等同 `ToJsonString()` |
114
+
115
+ ---
116
+
117
+ ## 29.3 Clay 型別 API 速查
118
+
119
+ ### 內建屬性
120
+
121
+ | 屬性 | 型別 | 說明 |
122
+ |------|------|------|
123
+ | `IsObject` / `IsArray` | `bool` | 型別判斷 |
124
+ | `Type` | `ClayType` | Object / Array |
125
+ | `Count` / `Length` | `int` | 鍵/元素數量 |
126
+ | `IsEmpty` | `bool` | 是否為空 |
127
+ | `Keys` | `IEnumerable<object>` | 鍵/索引列表 |
128
+ | `MemberNames` | `IEnumerable<string>` | 屬性名列表(僅單一物件) |
129
+ | `Values` | `IEnumerable<dynamic?>` | 值/元素列表 |
130
+ | `IsReadOnly` | `bool` | 是否唯讀 |
131
+
132
+ ### 標識符(運算子)
133
+
134
+ 支援:字串鍵、整數索引、`Index`(`^1`)、`Range`(`1..^2`)、路徑(`AppInfo:Company:Name`)、JSON Path(`$.AppInfo.Company`)。
135
+
136
+ ### 核心方法
137
+
138
+ | 方法 | 說明 |
139
+ |------|------|
140
+ | `Get(id)` / `Get<T>(id)` | 取值(支援型別轉換) |
141
+ | `Set(id, value)` | 設值 |
142
+ | `Contains(id)` / `IsDefined(id)` / `HasProperty(name)` | 檢查是否定義 |
143
+ | `FindNode(id)` / `FindNodeByPath(path)` | 查找 JsonNode 節點 |
144
+ | `Add(v)` / `Push(v)` / `Append(v)` / `AddRange(...)` | 追加(陣列) |
145
+ | `Insert(index, v)` / `InsertRange(index, ...)` | 插入(陣列) |
146
+ | `Pop()` | 移除末項(陣列) |
147
+ | `Remove(id)` / `Delete(id)` | 刪除 |
148
+ | `Reverse()` | 反轉 |
149
+ | `Slice(range)` / `this[Range]` | 截取(陣列) |
150
+ | `Combine(clay2, clay3)` | 合併多個流變物件 |
151
+ | `DeepClone()` | 深度克隆 |
152
+ | `Clear()` | 清空資料 |
153
+ | `AsReadOnly()` / `AsMutable()` | 唯讀/可寫模式切換 |
154
+ | `Rebuilt(options)` | 重建(套用新 ClayOptions) |
155
+ | `KSort()` / `KRSort()` | 按鍵升序/降序排序(單一物件) |
156
+ | `Extend(...)` | 擴充資料 |
157
+ | `IndexOf(value)` | 取得元素索引(陣列) |
158
+ | `Equals(other)` / `==` / `!=` | 相等比較 |
159
+ | `WriteTo(Utf8JsonWriter)` | 寫入 JsonWriter |
160
+
161
+ ### 路徑操作
162
+
163
+ ```csharp
164
+ clay.PathValue("AppInfo:Company:Name"); // 取值
165
+ clay.SetPathValue("AppInfo:Name", "新值"); // 設值(僅更新已存在路徑)
166
+ clay.RemovePathValue("AppInfo:Name"); // 刪除
167
+ clay.ContainsByPath("AppInfo:Name"); // 檢查
168
+ clay.FindNodeByPath("AppInfo:Company:Name"); // 查找 JsonNode
169
+ clay["AppInfo:Name", true]; // 路徑索引(第二參數 true)
170
+ ```
171
+
172
+ ### 遍歷與查詢
173
+
174
+ ```csharp
175
+ // ForEach
176
+ foreach (KeyValuePair<string, dynamic?> item in clay) { } // 單一物件
177
+ foreach (var item in array) { } // 陣列
178
+
179
+ // 解構函數(推薦用於 Lambda/Linq)
180
+ var (clay, enumerable) = Clay.Parse("...");
181
+ var list = enumerable.Where(u => u?.Key == "id").ToList();
182
+
183
+ // Map / Filter
184
+ clay.Map(new Func<dynamic?, object?>(item => new { data = item?.Value }));
185
+ clay.Filter(new Func<dynamic?, bool>(item => item?.Key != "id"));
186
+
187
+ // 管道轉換
188
+ dynamic data = Clay.Parse(json).Pipe(u => u.data);
189
+ dynamic data = Clay.Parse(json).PipeTry(u => u.data2).Pipe(u => u.data);
190
+ ```
191
+
192
+ ### 動態委託方法(僅單一物件)
193
+
194
+ ```csharp
195
+ clay.sayHello = (Func<string>)(() => $"Hello, {clay.name}!");
196
+ Console.WriteLine(clay.sayHello());
197
+
198
+ // 使用 ClayContext 避免閉包問題
199
+ clay.greet = (Func<ClayContext, string>)(ctx => $"Hello, {ctx.Current.name}!");
200
+ Console.WriteLine(clay.greet()); // 無需傳入 ClayContext
201
+ ```
202
+
203
+ ### 處理 JSON 雙重序列化
204
+
205
+ ```csharp
206
+ dynamic clay = Clay.Parse(json).ParseJson("fieldName"); // 指定路徑
207
+ dynamic clay = Clay.Parse(json).ParseJson(); // 自動遍歷(預設深度 3)
208
+ ```
209
+
210
+ ---
211
+
212
+ ## 29.4 ClayOptions 設定
213
+
214
+ | 屬性 | 型別 | 預設值 | 說明 |
215
+ |------|------|--------|------|
216
+ | `ScalarValueKey` | `string` | `"value"` | 非物件/陣列字面量的鍵名 |
217
+ | `AllowMissingProperty` | `bool` | `false` | 允許存取不存在的屬性(回傳 null) |
218
+ | `AllowIndexOutOfRange` | `bool` | `false` | 允許存取越界索引(回傳 null) |
219
+ | `AutoCreateNestedObjects` | `bool` | `false` | 自動建立巢狀物件 |
220
+ | `AutoCreateNestedArrays` | `bool` | `false` | 自動建立巢狀陣列 |
221
+ | `AutoExpandArrayWithNulls` | `bool` | `false` | 超出陣列長度時自動補 null |
222
+ | `ValidateAfterConversion` | `bool` | `false` | 轉換後執行模型驗證 |
223
+ | `DateJsonToDateTime` | `bool` | `false` | 日期字串自動轉 DateTime |
224
+ | `KeyValueJsonToObject` | `bool` | `false` | key/value JSON 轉單一物件 |
225
+ | `PropertyNameCaseInsensitive` | `bool` | `false` | 屬性名不區分大小寫 |
226
+ | `PathSeparator` | `string[]` | `[":"]` | 路徑分隔符 |
227
+ | `ReadOnly` | `bool` | `false` | 唯讀模式 |
228
+ | `JsonSerializerOptions` | — | 預設設定 | JSON 序列化選項 |
229
+
230
+ **快捷設定**:`ClayOptions.Flexible`(AllowMissingProperty + AllowIndexOutOfRange + PropertyNameCaseInsensitive 皆為 true)。
231
+
232
+ ---
233
+
234
+ ## 29.5 事件監聽
235
+
236
+ ```csharp
237
+ Clay clayObject = clay; // 需轉回 Clay 型別
238
+
239
+ clayObject.Changing += (sender, args) => { }; // 變更前
240
+ clayObject.Changed += (sender, args) => { }; // 變更後
241
+ clayObject.Removing += (sender, args) => { }; // 移除前
242
+ clayObject.Removed += (sender, args) => { }; // 移除後
243
+
244
+ // 或使用 AddEvent(無需型別轉換)
245
+ clay.AddEvent("Changed", new ClayEventHandler((sender, args) => { }));
246
+ clay.AddEvent("Changed", new Action<dynamic, ClayEventArgs>((sender, args) => { }));
247
+ ```
248
+
249
+ ---
250
+
251
+ ## 29.2.11 在 ASP.NET 應用中使用
252
+
253
+ ### 設定
254
+
255
+ ```csharp
256
+ services.AddControllers()
257
+ .AddClayOptions(options => { options.KeyValueJsonToObject = true; });
258
+ ```
259
+
260
+ ### WebAPI
261
+
262
+ ```csharp
263
+ [HttpPost]
264
+ public dynamic PostClay([Clay] dynamic clay) => clay;
265
+
266
+ [HttpPost]
267
+ public YourModel PostData()
268
+ {
269
+ dynamic clay = Clay.Parse("""{"id":1}""");
270
+ return clay; // 自動轉換為目標型別
271
+ }
272
+ ```
273
+
274
+ ### MVC
275
+
276
+ ```csharp
277
+ public IActionResult Index()
278
+ => View(Clay.Parse("""{"id":1,"name":"Furion"}"""));
279
+ // 視圖:@model dynamic → @Model.id
280
+ ```
281
+
282
+ ---
283
+
284
+ ## 29.7 HTTP 遠端請求中使用
285
+
286
+ ```csharp
287
+ // 設定轉換器
288
+ services.AddHttpRemote().ConfigureOptions(opt =>
289
+ opt.JsonSerializerOptions.AddClayConverters());
290
+
291
+ // 發送請求
292
+ dynamic payload = new Clay();
293
+ payload.id = 1;
294
+ var content = await httpRemoteService.PostAsStringAsync(url,
295
+ b => b.SetJsonContent(payload));
296
+ dynamic result = Clay.Parse(content);
297
+ ```
298
+
299
+ ---
300
+
301
+ ## 29.8 常見問題速查
302
+
303
+ | 問題 | 解法 |
304
+ |------|------|
305
+ | 屬性不存在拋異常 | `ClayOptions.Flexible` 或 `AllowMissingProperty = true` |
306
+ | 索引越界拋異常 | `AllowIndexOutOfRange = true` |
307
+ | 屬性大小寫敏感 | `PropertyNameCaseInsensitive = true` |
308
+ | C# 關鍵字鍵名 | 屬性方式加 `@`(如 `clay.@int`),索引方式不需要 |
309
+ | 中文亂碼 | `ToString("U")` 或 `ToJsonString()` |
310
+ | WebAPI 回傳 key/value 格式 | 新增 `.AddClayOptions()` |
311
+ | 捕獲溢出欄位 | 在目標型別加 `[JsonExtensionData] Dictionary<string, JsonElement>` |
312
+ | Newtonsoft.Json 支援 | `.AddNewtonsoftJson(opt => opt.SerializerSettings.Converters.AddClayConverters())` |
313
+ | Swagger Schema 問題 | 用 `<remarks>` + Markdown 描述參數格式 |
@@ -0,0 +1,168 @@
1
+ # Furion 脫敏處理(Sensitive Detection)筆記
2
+
3
+ > 適用版本:Furion 2.4.4+,部分功能需更高版本,詳見各節說明。
4
+
5
+ ---
6
+
7
+ ## 版本更新重點
8
+
9
+ | 類型 | 說明 | 版本 |
10
+ |------|------|------|
11
+ | 新增 | `[SensitiveDetection]` 特性支援 `ShowSensitiveWords` 屬性顯示命中敏感詞 | 4.9.8.10 |
12
+ | 新增 | `[SensitiveDetection]` 特性支援預設錯誤訊息 | 4.9.8.13 |
13
+ | 新增 | 全模組同步方法(`GetWords`、`IsValid`、`Replace`、`FoundSensitiveWords`) | 4.9.8.10 |
14
+ | 新增 | 支援自訂嵌入詞庫檔名 | 4.9.1.45 |
15
+ | 新增 | `FoundSensitiveWordsAsync`:取得敏感詞及位置 | 4.9.1.45 |
16
+ | **調整** | `VaildedAsync` 更名為 `IsValidAsync` | 4.9.8.9 |
17
+ | 修復 | `[SensitiveDetection]` 不支援格式符 `{0}` | 4.9.8.10 |
18
+ | 修復 | 跨平台換行符差異導致詞彙分割失敗 | 4.9.8.9 |
19
+ | 修復 | 程式集名自訂導致無法載入詞庫資源 | 4.9.7.119 |
20
+
21
+ ---
22
+
23
+ ## 1. 註冊服務
24
+
25
+ ```csharp
26
+ // 基本註冊
27
+ services.AddSensitiveDetection();
28
+
29
+ // 自訂詞庫檔名(4.9.1.45+)
30
+ services.AddSensitiveDetection(options =>
31
+ {
32
+ options.EmbedFileName = "custom-words.txt";
33
+ });
34
+
35
+ // 自訂 Provider
36
+ services.AddSensitiveDetection<YourSensitiveDetectionProvider>();
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 2. 詞庫檔案(sensitive-words.txt)
42
+
43
+ - 在 Web 啟動層建立 `sensitive-words.txt`
44
+ - 編碼:**UTF-8**(4.8.6.7+ 支援 UTF-8 BOM)
45
+ - 必須設定為**嵌入式資源**
46
+
47
+ ```
48
+ // 每行一個詞(換行格式)
49
+ 壞人
50
+ 無語
51
+ 滾開
52
+
53
+ // 或用 | 分隔(3.8.9+,節省空間)
54
+ 壞人|無語|滾開|八嘎
55
+ ```
56
+
57
+ > ⚠️ 若未設為嵌入式資源,詞庫將無法載入。
58
+
59
+ ---
60
+
61
+ ## 3. 使用方式
62
+
63
+ ### 3.1 Attribute 驗證(自動)
64
+
65
+ ```csharp
66
+ public class Content
67
+ {
68
+ // 基本用法
69
+ [SensitiveDetection]
70
+ public string Text { get; set; }
71
+
72
+ // 自訂錯誤訊息
73
+ [SensitiveDetection(ErrorMessage = "包含敏感詞")]
74
+ public string Title { get; set; }
75
+
76
+ // 顯示命中詞(4.9.8.10+)
77
+ [SensitiveDetection(
78
+ ErrorMessage = "{0} 包含敏感詞,命中:{1}",
79
+ ShowSensitiveWords = true)]
80
+ public string Comment { get; set; }
81
+
82
+ // 自動替換為指定字元(3.8.8+ 支援方法參數)
83
+ [SensitiveDetection('*')]
84
+ public string Body { get; set; }
85
+ }
86
+
87
+ // DynamicApi / Controller 參數
88
+ public void Post([SensitiveDetection] string text) { }
89
+ public void Post([SensitiveDetection('*')] string text) { }
90
+ ```
91
+
92
+ ### 3.2 ISensitiveDetectionProvider(手動)
93
+
94
+ ```csharp
95
+ public class YourService : IDynamicApiController
96
+ {
97
+ private readonly ISensitiveDetectionProvider _sdp;
98
+
99
+ public YourService(ISensitiveDetectionProvider sdp)
100
+ {
101
+ _sdp = sdp;
102
+ }
103
+
104
+ // 取得所有敏感詞
105
+ public async Task<IEnumerable<string>> GetWordsAsync()
106
+ => await _sdp.GetWordsAsync();
107
+
108
+ // 驗證:true = 正常,false = 命中敏感詞
109
+ public async Task<bool> IsValidAsync(string text)
110
+ => await _sdp.IsValidAsync(text);
111
+
112
+ // 替換命中詞為 *
113
+ public async Task<string> ReplaceAsync(string text)
114
+ => await _sdp.ReplaceAsync(text, '*');
115
+
116
+ // 取得命中詞與位置(4.9.1.45+)
117
+ public async Task<Dictionary<string, List<int>>> FoundAsync(string text)
118
+ => await _sdp.FoundSensitiveWordsAsync(text);
119
+ }
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 4. 介面方法速查
125
+
126
+ | 方法 | 回傳值 | 說明 |
127
+ |------|--------|------|
128
+ | `GetWordsAsync()` | `IEnumerable<string>` | 取得所有敏感詞清單 |
129
+ | `IsValidAsync(text)` | `bool` | `true` = 無敏感詞(正常) |
130
+ | `ReplaceAsync(text, '*')` | `string` | 將命中詞替換為指定字元 |
131
+ | `FoundSensitiveWordsAsync(text)` | `Dictionary<string, List<int>>` | 命中詞與其位置索引(4.9.1.45+) |
132
+
133
+ > 4.9.8.10+ 以上方法均有同步版本(去掉 `Async` 後綴)。
134
+
135
+ ---
136
+
137
+ ## 5. 自訂 Provider
138
+
139
+ ```csharp
140
+ public class DbSensitiveProvider : ISensitiveDetectionProvider
141
+ {
142
+ // 從資料庫取詞,建議搭配 MemoryCache / Redis
143
+ public async Task<IEnumerable<string>> GetWordsAsync()
144
+ {
145
+ // 實作:查 DB 或快取
146
+ }
147
+
148
+ // true = 正常,false = 命中敏感詞
149
+ public async Task<bool> IsValidAsync(string text)
150
+ {
151
+ // 實作:判斷邏輯
152
+ }
153
+
154
+ // 替換命中詞
155
+ public async Task<string> ReplaceAsync(string text, char transfer = '*')
156
+ {
157
+ // 實作:替換邏輯
158
+ }
159
+
160
+ // 查找詞與位置(4.9.1.45+)
161
+ public async Task<Dictionary<string, List<int>>> FoundSensitiveWordsAsync(string text)
162
+ {
163
+ // 實作:查找邏輯
164
+ }
165
+ }
166
+ ```
167
+
168
+ > 💡 `GetWordsAsync` 若從資料庫取詞,務必搭配快取,避免每次請求都查 DB。
@@ -0,0 +1,147 @@
1
+ # 32. 會話和狀態管理
2
+
3
+ ---
4
+
5
+ ## 32.1 概述
6
+
7
+ HTTP 是無狀態的協定,預設不保留使用者資料。可透過以下方式保留請求間的使用者資料:
8
+
9
+ | 方式 | 儲存位置 | 生命週期 |
10
+ |------|---------|---------|
11
+ | **Cookie** | 客戶端 | 可設定過期時間 |
12
+ | **Session** | 伺服器端(記憶體/分散式) | 閒置逾時後銷毀 |
13
+ | **Query Strings** | URL 參數 | 單次請求 |
14
+ | **HttpContext.Items** | 伺服器端 | 單次請求結束即銷毀 |
15
+ | **Cache** | 伺服器端 | 依快取策略 |
16
+ | **AsyncLocal\<T\>** | 非同步控制流 | 跨執行緒共享 |
17
+
18
+ ---
19
+
20
+ ## 32.2 使用方式
21
+
22
+ ### 32.2.1 Cookie
23
+
24
+ ```csharp
25
+ // 讀取
26
+ var value = httpContext.Request.Cookies["key"];
27
+
28
+ // 設定
29
+ var option = new CookieOptions { Expires = DateTime.Now.AddMilliseconds(10) };
30
+ httpContext.Response.Cookies.Append(key, value, option);
31
+
32
+ // 刪除
33
+ httpContext.Response.Cookies.Delete(key);
34
+ ```
35
+
36
+ > `httpContext` 可透過 `IHttpContextAccessor` 或 `App.HttpContext` 取得。Cookie 還可實現授權及單點登入(SSO)。
37
+
38
+ ### 32.2.2 Session
39
+
40
+ **註冊服務**(必須在控制器之前):
41
+
42
+ ```csharp
43
+ services.AddSession(options =>
44
+ {
45
+ options.IdleTimeout = TimeSpan.FromSeconds(10);
46
+ options.Cookie.HttpOnly = true;
47
+ options.Cookie.IsEssential = true;
48
+ });
49
+ ```
50
+
51
+ **註冊中介軟體**(必須在 `UseRouting` 和 `UseEndpoints` 之間):
52
+
53
+ ```csharp
54
+ app.UseRouting();
55
+ app.UseAuthentication();
56
+ app.UseAuthorization();
57
+ app.UseSession(); // ← 在這裡
58
+ app.UseEndpoints(...);
59
+ ```
60
+
61
+ **常見操作**:
62
+
63
+ ```csharp
64
+ // 讀取
65
+ var str = httpContext.Session.GetString("key");
66
+ var num = httpContext.Session.GetInt32("key");
67
+
68
+ // 設定
69
+ httpContext.Session.SetString("key", "value");
70
+ httpContext.Session.SetInt32("key", 1);
71
+ ```
72
+
73
+ **自訂任意型別擴充**:
74
+
75
+ ```csharp
76
+ public static class SessionExtensions
77
+ {
78
+ public static void Set<T>(this ISession session, string key, T value)
79
+ => session.SetString(key, JsonSerializer.Serialize(value));
80
+
81
+ public static T Get<T>(this ISession session, string key)
82
+ {
83
+ var value = session.GetString(key);
84
+ return value == null ? default : JsonSerializer.Deserialize<T>(value);
85
+ }
86
+ }
87
+ ```
88
+
89
+ **防止 Session ID 改變或失效**:
90
+
91
+ ```csharp
92
+ services.Configure<CookiePolicyOptions>(options =>
93
+ {
94
+ options.CheckConsentNeeded = context => false;
95
+ options.MinimumSameSitePolicy = SameSiteMode.None;
96
+ });
97
+ ```
98
+
99
+ ### 32.2.3 Query Strings
100
+
101
+ ```csharp
102
+ var value = httpContext.Request.Query["key"];
103
+ ```
104
+
105
+ ### 32.2.4 HttpContext.Items
106
+
107
+ 單次請求間共享資料,請求結束立即銷毀,可儲存任何型別:
108
+
109
+ ```csharp
110
+ // 讀取
111
+ var value = httpContext.Items["key"];
112
+
113
+ // 設定
114
+ httpContext.Items["key"] = "任何值包括物件";
115
+
116
+ // 刪除
117
+ httpContext.Items.Remove("key");
118
+ ```
119
+
120
+ ### 32.2.5 Cache
121
+
122
+ 參見 **14. 分散式快取** 章節。
123
+
124
+ ### 32.2.6 AsyncLocal\<T\>
125
+
126
+ 跨執行緒、非同步控制流中共享資料的利器:
127
+
128
+ ```csharp
129
+ static AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();
130
+
131
+ _asyncLocalString.Value = "Value 1";
132
+ // 在非同步方法中仍可正確讀取 "Value 1"
133
+ ```
134
+
135
+ > `AsyncLocal<T>` 會隨非同步控制流傳遞值,而 `ThreadLocal<T>` 在 `await` 之後可能丟失值。
136
+
137
+ **Furion 簡化用法**(v2.18+):
138
+
139
+ ```csharp
140
+ // 設定
141
+ CallContext.SetLocalValue("name", "Furion");
142
+ CallContext<int>.SetLocalValue("count", 1);
143
+
144
+ // 讀取
145
+ var name = CallContext.GetLocalValue("name");
146
+ var count = CallContext<int>.GetLocalValue("count");
147
+ ```