@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
package/dist/index.js
CHANGED
|
@@ -4,23 +4,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
5
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const event_js_1 = require("./knowledge/event.js");
|
|
13
|
-
const attributes_js_1 = require("./knowledge/attributes.js");
|
|
14
|
-
const vue_typescript_js_1 = require("./knowledge/vue-typescript.js");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
function loadMd(filename) {
|
|
10
|
+
return (0, fs_1.readFileSync)((0, path_1.join)(__dirname, "../knowledge", filename), "utf-8");
|
|
11
|
+
}
|
|
15
12
|
const KNOWLEDGE_MAP = {
|
|
16
|
-
entity:
|
|
17
|
-
service:
|
|
18
|
-
sqlsugar:
|
|
19
|
-
config:
|
|
20
|
-
plugin:
|
|
21
|
-
event:
|
|
22
|
-
attributes:
|
|
23
|
-
"vue-typescript":
|
|
13
|
+
entity: loadMd("entity.md"),
|
|
14
|
+
service: loadMd("service.md"),
|
|
15
|
+
sqlsugar: loadMd("sqlsugar.md"),
|
|
16
|
+
config: loadMd("config.md"),
|
|
17
|
+
plugin: loadMd("plugin.md"),
|
|
18
|
+
event: loadMd("event.md"),
|
|
19
|
+
attributes: loadMd("attributes.md"),
|
|
20
|
+
"vue-typescript": loadMd("vue-typescript.md"),
|
|
24
21
|
};
|
|
25
22
|
const TOPIC_DESCRIPTIONS = {
|
|
26
23
|
entity: "實體基礎類別階層(EntityBase, EntityBaseDel, EntityBaseTenant...)",
|
|
@@ -36,111 +33,109 @@ const server = new mcp_js_1.McpServer({
|
|
|
36
33
|
name: "admin-net-knowledge",
|
|
37
34
|
version: "1.0.0",
|
|
38
35
|
});
|
|
39
|
-
// 各主題工具(固定回傳對應知識)
|
|
40
36
|
const topicTools = [
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
topic: "service",
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: "get_sqlsugar_guide",
|
|
53
|
-
description: "取得 SqlSugar ORM 指南:AOP 自動填充欄位(CreateTime/UpdateTime/TenantId)、Repository 查詢語法、交易管理([UnitOfWork])",
|
|
54
|
-
topic: "sqlsugar",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: "get_config_guide",
|
|
58
|
-
description: "取得 Admin.NET 設定系統指南:IConfigurableOptions 用法、JSON 設定檔位置(Database.json、App.json)、多租戶策略",
|
|
59
|
-
topic: "config",
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: "get_plugin_guide",
|
|
63
|
-
description: "取得 Admin.NET 插件系統指南:[AppStartup(N)] 優先順序、插件目錄結構、ConfigureServices 範例、現有插件清單",
|
|
64
|
-
topic: "plugin",
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: "get_event_guide",
|
|
68
|
-
description: "取得事件匯流排指南:[EventSubscribe] 訂閱者寫法、IEventSubscriber 必要條件、發布事件語法、系統內建事件常數",
|
|
69
|
-
topic: "event",
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: "get_attribute_guide",
|
|
73
|
-
description: "取得自訂 Attribute 參考表:[Idempotent] 防重複、[DataMask] 遮罩、[Dict] 字典驗證、[SeedData] 種子資料等 20+ 個 Attribute",
|
|
74
|
-
topic: "attributes",
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
name: "get_vue_typescript_guide",
|
|
78
|
-
description: "取得 Vue 3 + TypeScript 採坑記錄:響應式陷阱、Element Plus 常見問題、Vite 模組系統、Pinia 狀態管理、Snowflake ID 精度問題",
|
|
79
|
-
topic: "vue-typescript",
|
|
80
|
-
},
|
|
37
|
+
{ name: "get_entity_guide", description: "取得 Admin.NET 實體(Entity)設計指南:EntityBase 類別階層、各屬性說明、如何選擇基礎類別、過濾介面(IDeletedFilter/ITenantIdFilter)", topic: "entity" },
|
|
38
|
+
{ name: "get_service_guide", description: "取得 Admin.NET Service 設計指南:IDynamicApiController 模式、[ApiDescriptionSettings]、BaseService<T> 泛型 CRUD、DTO 命名慣例", topic: "service" },
|
|
39
|
+
{ name: "get_sqlsugar_guide", description: "取得 SqlSugar ORM 指南:AOP 自動填充欄位(CreateTime/UpdateTime/TenantId)、Repository 查詢語法、交易管理([UnitOfWork])", topic: "sqlsugar" },
|
|
40
|
+
{ name: "get_config_guide", description: "取得 Admin.NET 設定系統指南:IConfigurableOptions 用法、JSON 設定檔位置(Database.json、App.json)、多租戶策略", topic: "config" },
|
|
41
|
+
{ name: "get_plugin_guide", description: "取得 Admin.NET 插件系統指南:[AppStartup(N)] 優先順序、插件目錄結構、ConfigureServices 範例、現有插件清單", topic: "plugin" },
|
|
42
|
+
{ name: "get_event_guide", description: "取得事件匯流排指南:[EventSubscribe] 訂閱者寫法、IEventSubscriber 必要條件、發布事件語法、系統內建事件常數", topic: "event" },
|
|
43
|
+
{ name: "get_attribute_guide", description: "取得自訂 Attribute 參考表:[Idempotent] 防重複、[DataMask] 遮罩、[Dict] 字典驗證、[SeedData] 種子資料等 20+ 個 Attribute", topic: "attributes" },
|
|
44
|
+
{ name: "get_vue_typescript_guide", description: "取得 Vue 3 + TypeScript 採坑記錄:響應式陷阱、Element Plus 常見問題、Vite 模組系統、Pinia 狀態管理、Snowflake ID 精度問題", topic: "vue-typescript" },
|
|
81
45
|
];
|
|
82
|
-
// 註冊所有主題工具
|
|
83
46
|
for (const { name, description, topic } of topicTools) {
|
|
84
47
|
server.registerTool(name, { description }, async () => ({
|
|
85
48
|
content: [{ type: "text", text: KNOWLEDGE_MAP[topic] }],
|
|
86
49
|
}));
|
|
87
50
|
}
|
|
88
|
-
// 列出所有主題
|
|
89
51
|
server.registerTool("list_topics", { description: "列出所有可用的知識主題及其簡短說明" }, async () => {
|
|
90
|
-
const lines = Object.entries(TOPIC_DESCRIPTIONS).map(([
|
|
52
|
+
const lines = Object.entries(TOPIC_DESCRIPTIONS).map(([k, d]) => `- **${k}**: ${d}`);
|
|
91
53
|
return {
|
|
92
|
-
content: [
|
|
93
|
-
{
|
|
94
|
-
type: "text",
|
|
95
|
-
text: `# Admin.NET + Vue 知識庫主題\n\n${lines.join("\n")}\n\n使用對應的 get_*_guide 工具取得詳細說明。`,
|
|
96
|
-
},
|
|
97
|
-
],
|
|
54
|
+
content: [{ type: "text", text: `# Admin.NET + Vue 知識庫主題\n\n${lines.join("\n")}\n\n使用對應的 get_*_guide 工具取得詳細說明。` }],
|
|
98
55
|
};
|
|
99
56
|
});
|
|
100
|
-
// 跨主題搜尋
|
|
101
57
|
server.registerTool("search_knowledge", {
|
|
102
58
|
description: "在所有知識庫中搜尋關鍵字,回傳包含該關鍵字的相關說明段落",
|
|
103
|
-
inputSchema: { keyword: zod_1.z.string().describe("
|
|
59
|
+
inputSchema: { keyword: zod_1.z.string().describe("搜尋關鍵字(例如:TenantId、軟刪除、ref、el-dialog)") },
|
|
104
60
|
}, async ({ keyword }) => {
|
|
105
61
|
const kw = keyword.toLowerCase();
|
|
106
62
|
const results = [];
|
|
107
63
|
for (const [topic, content] of Object.entries(KNOWLEDGE_MAP)) {
|
|
108
64
|
const lines = content.split("\n");
|
|
109
|
-
const
|
|
65
|
+
const blocks = [];
|
|
110
66
|
let i = 0;
|
|
111
67
|
while (i < lines.length) {
|
|
112
68
|
if (lines[i].toLowerCase().includes(kw)) {
|
|
113
69
|
const start = Math.max(0, i - 2);
|
|
114
70
|
const end = Math.min(lines.length - 1, i + 4);
|
|
115
|
-
|
|
71
|
+
blocks.push(lines.slice(start, end + 1).join("\n"));
|
|
116
72
|
i = end + 1;
|
|
117
73
|
}
|
|
118
74
|
else {
|
|
119
75
|
i++;
|
|
120
76
|
}
|
|
121
77
|
}
|
|
122
|
-
if (
|
|
123
|
-
results.push(`## 來自:${topic}(${TOPIC_DESCRIPTIONS[topic]})\n\n${
|
|
78
|
+
if (blocks.length > 0) {
|
|
79
|
+
results.push(`## 來自:${topic}(${TOPIC_DESCRIPTIONS[topic]})\n\n${blocks.slice(0, 3).join("\n---\n")}`);
|
|
124
80
|
}
|
|
125
81
|
}
|
|
126
82
|
if (results.length === 0) {
|
|
127
|
-
return {
|
|
128
|
-
content: [
|
|
129
|
-
{
|
|
130
|
-
type: "text",
|
|
131
|
-
text: `未找到包含「${keyword}」的內容。\n\n可用主題:${Object.keys(KNOWLEDGE_MAP).join("、")}`,
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
};
|
|
83
|
+
return { content: [{ type: "text", text: `未找到「${keyword}」。可用主題:${Object.keys(KNOWLEDGE_MAP).join("、")}` }] };
|
|
135
84
|
}
|
|
136
|
-
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
85
|
+
return { content: [{ type: "text", text: `# 搜尋結果:「${keyword}」\n\n${results.join("\n\n---\n\n")}` }] };
|
|
86
|
+
});
|
|
87
|
+
// ── Furion 教學文件工具 ──────────────────────────────────────────
|
|
88
|
+
const FURION_DIR = (0, path_1.join)(__dirname, "../knowledge/Furion_Teaching_Manual");
|
|
89
|
+
server.registerTool("list_furion_docs", { description: "列出所有 Furion 教學文件的檔名,用於了解有哪些主題可查詢" }, async () => {
|
|
90
|
+
const files = (0, fs_1.readdirSync)(FURION_DIR).filter(f => f.endsWith(".md")).sort();
|
|
91
|
+
const list = files.map(f => `- ${f.replace(".md", "")}`).join("\n");
|
|
92
|
+
return { content: [{ type: "text", text: `# Furion 教學文件清單(${files.length} 份)\n\n${list}\n\n使用 get_furion_doc 取得完整內容,或用 search_furion_docs 搜尋關鍵字。` }] };
|
|
93
|
+
});
|
|
94
|
+
server.registerTool("get_furion_doc", {
|
|
95
|
+
description: "取得指定 Furion 教學文件的完整內容。先用 list_furion_docs 取得檔名清單。",
|
|
96
|
+
inputSchema: { filename: zod_1.z.string().describe("檔名(例如:10-1-SqlSugar整合 或 22-事件匯流排EventBus,不需要 .md 副檔名)") },
|
|
97
|
+
}, async ({ filename }) => {
|
|
98
|
+
const target = filename.endsWith(".md") ? filename : `${filename}.md`;
|
|
99
|
+
const files = (0, fs_1.readdirSync)(FURION_DIR);
|
|
100
|
+
const match = files.find(f => f === target || f.toLowerCase() === target.toLowerCase());
|
|
101
|
+
if (!match) {
|
|
102
|
+
const suggestions = files.filter(f => f.toLowerCase().includes(filename.toLowerCase().replace(".md", ""))).slice(0, 5);
|
|
103
|
+
const hint = suggestions.length > 0 ? `\n\n相似檔名:\n${suggestions.map(f => `- ${f}`).join("\n")}` : "";
|
|
104
|
+
return { content: [{ type: "text", text: `找不到「${filename}」。${hint}\n\n使用 list_furion_docs 查看完整清單。` }] };
|
|
105
|
+
}
|
|
106
|
+
const content = (0, fs_1.readFileSync)((0, path_1.join)(FURION_DIR, match), "utf-8");
|
|
107
|
+
return { content: [{ type: "text", text: content }] };
|
|
108
|
+
});
|
|
109
|
+
server.registerTool("search_furion_docs", {
|
|
110
|
+
description: "在所有 Furion 教學文件中搜尋關鍵字,回傳包含該關鍵字的段落和來源檔案",
|
|
111
|
+
inputSchema: { keyword: zod_1.z.string().describe("搜尋關鍵字(例如:動態API、多租戶、SqlSugar、EventBus)") },
|
|
112
|
+
}, async ({ keyword }) => {
|
|
113
|
+
const kw = keyword.toLowerCase();
|
|
114
|
+
const files = (0, fs_1.readdirSync)(FURION_DIR).filter(f => f.endsWith(".md")).sort();
|
|
115
|
+
const results = [];
|
|
116
|
+
for (const file of files) {
|
|
117
|
+
const lines = (0, fs_1.readFileSync)((0, path_1.join)(FURION_DIR, file), "utf-8").split("\n");
|
|
118
|
+
const blocks = [];
|
|
119
|
+
let i = 0;
|
|
120
|
+
while (i < lines.length) {
|
|
121
|
+
if (lines[i].toLowerCase().includes(kw)) {
|
|
122
|
+
const start = Math.max(0, i - 1);
|
|
123
|
+
const end = Math.min(lines.length - 1, i + 5);
|
|
124
|
+
blocks.push(lines.slice(start, end + 1).join("\n"));
|
|
125
|
+
i = end + 1;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
i++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (blocks.length > 0) {
|
|
132
|
+
results.push(`## ${file.replace(".md", "")}\n\n${blocks.slice(0, 2).join("\n---\n")}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (results.length === 0) {
|
|
136
|
+
return { content: [{ type: "text", text: `未找到「${keyword}」。使用 list_furion_docs 瀏覽所有主題。` }] };
|
|
137
|
+
}
|
|
138
|
+
return { content: [{ type: "text", text: `# Furion 文件搜尋:「${keyword}」(${results.length} 個檔案有結果)\n\n${results.join("\n\n---\n\n")}` }] };
|
|
144
139
|
});
|
|
145
140
|
async function main() {
|
|
146
141
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
# 4.1 配置
|
|
2
|
+
|
|
3
|
+
## 自定义配置特别注意
|
|
4
|
+
|
|
5
|
+
如 `xxxx.json`,必须在 VS Studio 中右键属性设置**复制输出目录**为 `如果较新则复制`,**生成操作**为 `内容`。
|
|
6
|
+
|
|
7
|
+
如果 `.json` 文件配置在多个项目层,也必须保证命名唯一,不然编译后出现相互覆盖的情况。
|
|
8
|
+
|
|
9
|
+
> **中文乱码问题**:默认情况下 `.json` 文件并未采用 `utf-8` 编码,所以如果存在中文读取后就会出现乱码情况,只需要修改 `.json` 文件编码为 `utf-8` 即可。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 4.1.1 什么是配置
|
|
14
|
+
|
|
15
|
+
简单来说,配置将系统应用可动态调配的选项放在统一地方管理,通过不同的配置让系统做出动态调整。
|
|
16
|
+
|
|
17
|
+
在 ASP.NET Core 应用程序启动时默认加载启动项目下的 `appsettings.json` 作为应用配置。同时还支持不同的运行环境加载对应的配置文件:
|
|
18
|
+
|
|
19
|
+
- **Development**:加载 `appsettings.Development.json`
|
|
20
|
+
- **Staging**:加载 `appsettings.Staging.json`
|
|
21
|
+
- **{Environment}**:`appsettings.{Environment}.json`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 4.1.2 配置的使用
|
|
26
|
+
|
|
27
|
+
假设我们需要在系统运行时获取系统名称、版本号及版权信息,这些信息可能随时变化而且需要在多个地方使用。
|
|
28
|
+
|
|
29
|
+
### 4.1.2.1 配置 appsettings.json 信息
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"AppInfo": {
|
|
34
|
+
"Name": "Furion",
|
|
35
|
+
"Version": "1.0.0",
|
|
36
|
+
"Company": "Baiqian"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> **特别注意**:`appsettings.json` 复制输出目录为 `如果较新则复制`,生成操作为 `内容`。
|
|
42
|
+
>
|
|
43
|
+
> 另外,某些 Linux 系统不支持读取带注释的 JSON 文件,直接读取将会报错。需要将 JSON 内的注释全部删除才能正常读取。
|
|
44
|
+
|
|
45
|
+
### 4.1.2.2 读取 appsettings.json 信息
|
|
46
|
+
|
|
47
|
+
在 Furion 框架中,提供了两种读取方式:
|
|
48
|
+
|
|
49
|
+
**方式一:通过 `App.Configuration[path]` 读取**
|
|
50
|
+
|
|
51
|
+
```csharp
|
|
52
|
+
using Microsoft.AspNetCore.Mvc;
|
|
53
|
+
|
|
54
|
+
namespace Furion.Web.Entry.Controllers
|
|
55
|
+
{
|
|
56
|
+
[Route("api/[controller]")]
|
|
57
|
+
public class DefaultController : ControllerBase
|
|
58
|
+
{
|
|
59
|
+
[HttpGet]
|
|
60
|
+
public string Get()
|
|
61
|
+
{
|
|
62
|
+
return $@"名称:{App.Configuration["AppInfo:Name"]},
|
|
63
|
+
版本:{App.Configuration["AppInfo:Version"]},
|
|
64
|
+
公司:{App.Configuration["AppInfo:Company"]}";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**方式二:依赖注入 `IConfiguration` 对象读取**
|
|
71
|
+
|
|
72
|
+
```csharp
|
|
73
|
+
// 构造函数注入方式
|
|
74
|
+
private readonly IConfiguration _configuration;
|
|
75
|
+
public DefaultController(IConfiguration configuration)
|
|
76
|
+
{
|
|
77
|
+
_configuration = configuration;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 参数注入方式
|
|
81
|
+
public string Get([FromServices] IConfiguration configuration)
|
|
82
|
+
{
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 属性注入方式
|
|
86
|
+
public IConfiguration Configuration { get; set; }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 4.1.2.3 如何选择读取方式
|
|
90
|
+
|
|
91
|
+
- 在可依赖注入类中,依赖注入 `IConfiguration` 读取
|
|
92
|
+
- 在静态类/非依赖注入类中,选择 `App.Configuration[path]` 读取
|
|
93
|
+
|
|
94
|
+
### 4.1.2.4 读取配置并转换成特定类型
|
|
95
|
+
|
|
96
|
+
```csharp
|
|
97
|
+
// ASP.NET Core
|
|
98
|
+
var data = Configuration.GetSection("配置节点").Get<类型>();
|
|
99
|
+
|
|
100
|
+
// Furion,推荐!!!
|
|
101
|
+
var data = App.GetConfig<类型>("配置节点");
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 4.1.3 路径符查找节点
|
|
107
|
+
|
|
108
|
+
在 ASP.NET Core 中,配置采用 `:` 分隔符来读取分层配置数据。如 `AppInfo:Name`。如有更多层级数据则只需要通过 `:` 进入下一层节点即可。
|
|
109
|
+
|
|
110
|
+
假设有以下配置信息:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"AppInfo": {
|
|
115
|
+
"Name": "Furion",
|
|
116
|
+
"Version": "1.0.0",
|
|
117
|
+
"Company": {
|
|
118
|
+
"Name": "Baiqian",
|
|
119
|
+
"Address": {
|
|
120
|
+
"City": "中国",
|
|
121
|
+
"Province": "广东省",
|
|
122
|
+
"Detail": "中山市东区紫马公园西门"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
读取示例:
|
|
130
|
+
|
|
131
|
+
```csharp
|
|
132
|
+
var name = App.Configuration["AppInfo:Name"]; // => Furion
|
|
133
|
+
var companyName = App.Configuration["AppInfo:Company:Name"]; // => Baiqian
|
|
134
|
+
var city = App.Configuration["AppInfo:Company:Address:City"]; // => 中国
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 4.1.3.1 查找数组节点
|
|
138
|
+
|
|
139
|
+
可以通过 `App.Configuration["array:0"]` 获取,`0` 是索引数字。
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 4.1.4 自定义配置文件
|
|
144
|
+
|
|
145
|
+
> **XML 配置说明**:Furion v2.8.0 版本移除了 `.xml` 文件自动扫描配置,改为手动添加配置。
|
|
146
|
+
|
|
147
|
+
大多情况下,配置只需要在 `appsettings.json` 中配置即可,但一些特殊情况下可能需要独立的配置文件。Furion 目前支持 `.json` 和 `.xml` 两种方式。
|
|
148
|
+
|
|
149
|
+
**JSON 示例:**
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
// Furion.Web.Entry/emailsetting.json
|
|
153
|
+
{
|
|
154
|
+
"outlook": {
|
|
155
|
+
"smtp": {
|
|
156
|
+
"server": "smtp.office365.com",
|
|
157
|
+
"port": "587",
|
|
158
|
+
"ssl": "STARTTLS"
|
|
159
|
+
},
|
|
160
|
+
"pop": {
|
|
161
|
+
"server": "outlook.office365.com",
|
|
162
|
+
"port": "995",
|
|
163
|
+
"ssl": "TLS"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**XML 示例:**
|
|
170
|
+
|
|
171
|
+
```xml
|
|
172
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
173
|
+
<configuration>
|
|
174
|
+
<MyKey>MyXMLFile Value</MyKey>
|
|
175
|
+
<Position>
|
|
176
|
+
<Title>Title from MyXMLFile</Title>
|
|
177
|
+
<Name>Name from MyXMLFile</Name>
|
|
178
|
+
</Position>
|
|
179
|
+
</configuration>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
> **XML 配置事项**:如果采用 XML 配置,那么文件名必须以 `.config.xml` 结尾(不区分大小写)。
|
|
183
|
+
|
|
184
|
+
> **特别说明**:Furion 框架会在启动时自动扫描每一个项目层根目录下的 `*.json` 文件加入配置中,所以无需手工配置。新增 `*.json` 文件的属性**复制到输出目录**设置为 `始终复制` 或 `较新复制`,否则不会载入。另外配置文件不能出现重名。
|
|
185
|
+
|
|
186
|
+
在 v2.16.7+ 版本之后,支持自定义配置扫描目录:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"ConfigurationScanDirectories": ["目录1名称", "目录1名称/子目录名称"]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 4.1.4.1 读取 emailsetting.json 配置
|
|
195
|
+
|
|
196
|
+
读取自定义配置文件和读取 `appsettings.json` 一致,系统会自动从多个配置文件中读取:
|
|
197
|
+
|
|
198
|
+
```csharp
|
|
199
|
+
var smtpServer = App.Configuration["outlook:smtp:server"]; // => smtp.office365.com
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 4.1.4.2 排除特定配置文件
|
|
203
|
+
|
|
204
|
+
在启动层 `appsettings.json` 中添加 `IgnoreConfigurationFiles` 节点:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"IgnoreConfigurationFiles": ["runtime.json"]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
> **通配符支持**(Furion 4.8.8.22+):
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"IgnoreConfigurationFiles": ["seed_*.json"]
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 4.1.5 不同环境读取
|
|
223
|
+
|
|
224
|
+
在实际应用开发中,可能会根据不同的环境加载不同的配置文件,只需要遵循特定命名规范 `{name}.{Environment}.json` 即可:
|
|
225
|
+
|
|
226
|
+
- `appsettings.Development.json`
|
|
227
|
+
- `appsettings.Staging.json`
|
|
228
|
+
- `appsettings.Production.json`
|
|
229
|
+
- `emailsetting.Development.json`
|
|
230
|
+
- `emailsetting.Staging.json`
|
|
231
|
+
- `emailsetting.Production.json`
|
|
232
|
+
|
|
233
|
+
ASP.NET Core 会在应用启动时自动加载不同环境的配置文件。
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 4.1.6 配置更改通知(热更新)
|
|
238
|
+
|
|
239
|
+
在 .NET Core 应用程序中,配置支持更改通知,也就是热更新操作。一旦监听到配置文件发生变动,就会触发 `OnChange` 方法:
|
|
240
|
+
|
|
241
|
+
```csharp
|
|
242
|
+
var appInfoConfiguration = App.Configuration.GetSection("AppInfo");
|
|
243
|
+
ChangeToken.OnChange(() => App.Configuration.GetReloadToken(), () =>
|
|
244
|
+
{
|
|
245
|
+
var name = appInfoConfiguration["Name"]; // 实时的最新值
|
|
246
|
+
var version = appInfoConfiguration["Version"]; // 实时的最新值
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// 默认情况下,ChangeToken.OnChange 可能会触发两次,
|
|
250
|
+
// 所以在 Furion v4.9.6+ 可更改为以下方式
|
|
251
|
+
ChangeToken.OnChange(() => App.Configuration.GetReloadToken(), ((Action)(() =>
|
|
252
|
+
{
|
|
253
|
+
var name = appInfoConfiguration["Name"];
|
|
254
|
+
var version = appInfoConfiguration["Version"];
|
|
255
|
+
})).Debounce()); // 添加了防抖操作
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> **监听对象**:如果监听全局配置文件传入 `App.Configuration.GetReloadToken()`,如果只需要监听特定节点,传入 `App.Configuration.GetSection("AppInfo")`。
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## 4.1.7 手动添加配置文件
|
|
263
|
+
|
|
264
|
+
> **获取路径说明**:
|
|
265
|
+
> - 获取项目目录:`AppContext.BaseDirectory`
|
|
266
|
+
> - 获取网站根目录:`Directory.GetCurrentDirectory()`
|
|
267
|
+
|
|
268
|
+
有些时候配置文件没有放在项目的根目录下,需要手动载入。
|
|
269
|
+
|
|
270
|
+
**方式一:appsettings.json 中(推荐,v2.16.7+)**
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"ConfigurationScanDirectories": ["目录1名称", "目录1名称/子目录名称"]
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**方式二:.NET5 中 Program.cs 中配置**
|
|
279
|
+
|
|
280
|
+
```csharp
|
|
281
|
+
public class Program
|
|
282
|
+
{
|
|
283
|
+
public static void Main(string[] args)
|
|
284
|
+
{
|
|
285
|
+
CreateHostBuilder(args).Build().Run();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
|
289
|
+
Host.CreateDefaultBuilder(args)
|
|
290
|
+
.ConfigureAppConfiguration((hostingContext, config) =>
|
|
291
|
+
{
|
|
292
|
+
config.AddJsonFile("MyConfig.json", optional: true, reloadOnChange: true);
|
|
293
|
+
})
|
|
294
|
+
.ConfigureWebHostDefaults(webBuilder =>
|
|
295
|
+
{
|
|
296
|
+
webBuilder.UseStartup<Startup>();
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**方式三:.NET6 中 Program.cs 中配置**
|
|
302
|
+
|
|
303
|
+
```csharp
|
|
304
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
305
|
+
builder.Configuration.AddJsonFile("MyConfig.json", optional: true, reloadOnChange: true);
|
|
306
|
+
// 注意先添加配置再初始化 Furion
|
|
307
|
+
builder.Inject();
|
|
308
|
+
var app = builder.Build();
|
|
309
|
+
app.Run();
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
如果使用 `Serve.Run()` 模式:
|
|
313
|
+
|
|
314
|
+
```csharp
|
|
315
|
+
Serve.Run(RunOptions.Default.ConfigureConfiguration((env, configuration) => {
|
|
316
|
+
configuration.AddJsonFile("MyConfig.json", optional: true, reloadOnChange: true);
|
|
317
|
+
}));
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## 4.1.8 配置的优缺点
|
|
323
|
+
|
|
324
|
+
**优点:**
|
|
325
|
+
|
|
326
|
+
- 能够在系统运行时快速读取
|
|
327
|
+
- 无需额外配置
|
|
328
|
+
|
|
329
|
+
**缺点:**
|
|
330
|
+
|
|
331
|
+
- 存在重复读取
|
|
332
|
+
- 通过硬编码字符串读取,容易出错
|
|
333
|
+
- 不能设置默认值
|
|
334
|
+
- 不能在运行环境中动态配置
|
|
335
|
+
- 不能验证配置有效性
|
|
336
|
+
- 不支持更改通知
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 4.1.9 配置使用场景
|
|
341
|
+
|
|
342
|
+
如果只需要一次性读取配置信息,则使用配置,否则应该使用**选项(4.2 选项)**代替。
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 4.1.10 实现配置中心
|
|
347
|
+
|
|
348
|
+
ASP.NET Core 除了通过配置文件读取配置信息外,还支持自定义配置提供程序,通过配置提供程序可以实现配置中心,比如通过数据库提供配置。
|
|
349
|
+
|
|
350
|
+
具体实现查看微软官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#custom-configuration-provider
|
|
351
|
+
|
|
352
|
+
> **使用 Nacos**:若使用 Nacos 可参考相关 Issue:https://gitee.com/dotnetchina/Furion/issues/I6G02W
|
|
353
|
+
|
|
354
|
+
### 集成 AgileConfig 示例
|
|
355
|
+
|
|
356
|
+
> **版本说明**:仅限 Furion 4.4.9+ 版本使用。
|
|
357
|
+
|
|
358
|
+
**Serve.Run 方式:**
|
|
359
|
+
|
|
360
|
+
```csharp
|
|
361
|
+
Serve.Run(RunOptions.Default.WithArgs(args)
|
|
362
|
+
.ConfigureInject((builder, options) =>
|
|
363
|
+
{
|
|
364
|
+
options.ConfigureAppConfiguration((_, cfb) =>
|
|
365
|
+
{
|
|
366
|
+
cfb.AddAgileConfig(new AgileConfig.Client.ConfigClient(builder.Configuration), obj =>
|
|
367
|
+
{
|
|
368
|
+
Console.WriteLine($"{obj}");
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
options.ConfigureWebServices((_, services) =>
|
|
373
|
+
{
|
|
374
|
+
services.AddAgileConfig();
|
|
375
|
+
});
|
|
376
|
+
})
|
|
377
|
+
);
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**.NET6+ 方式:**
|
|
381
|
+
|
|
382
|
+
```csharp
|
|
383
|
+
var builder = WebApplication.CreateBuilder(args)
|
|
384
|
+
.Inject((builder, options) =>
|
|
385
|
+
{
|
|
386
|
+
options.ConfigureAppConfiguration((_, cfb) =>
|
|
387
|
+
{
|
|
388
|
+
cfb.AddAgileConfig(new AgileConfig.Client.ConfigClient(builder.Configuration), obj =>
|
|
389
|
+
{
|
|
390
|
+
Console.WriteLine($"{obj}");
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
options.ConfigureWebServices((_, services) =>
|
|
394
|
+
{
|
|
395
|
+
services.AddAgileConfig();
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**.NET5 方式:**
|
|
401
|
+
|
|
402
|
+
```csharp
|
|
403
|
+
public class Program
|
|
404
|
+
{
|
|
405
|
+
public static void Main(string[] args)
|
|
406
|
+
{
|
|
407
|
+
CreateHostBuilder(args).Build().Run();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
|
411
|
+
Host.CreateDefaultBuilder(args)
|
|
412
|
+
.ConfigureWebHostDefaults(webBuilder =>
|
|
413
|
+
{
|
|
414
|
+
webBuilder
|
|
415
|
+
.Inject((builder, options) =>
|
|
416
|
+
{
|
|
417
|
+
options.ConfigureAppConfiguration((_, cfb) =>
|
|
418
|
+
{
|
|
419
|
+
cfb.AddAgileConfig(new AgileConfig.Client.ConfigClient(cfb.Build()), obj =>
|
|
420
|
+
{
|
|
421
|
+
Console.WriteLine($"{obj}");
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
options.ConfigureWebServices((_, services) =>
|
|
425
|
+
{
|
|
426
|
+
services.AddAgileConfig();
|
|
427
|
+
});
|
|
428
|
+
})
|
|
429
|
+
.UseStartup<Startup>();
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## 4.1.11 重载配置
|
|
437
|
+
|
|
438
|
+
Furion 会在应用启动的时候对 `IConfiguration` 进行静态缓存,如果使用了 `App.Configuration` 静态属性且配置数据已发生变更,则调用以下方法刷新即可:
|
|
439
|
+
|
|
440
|
+
```csharp
|
|
441
|
+
App.Configuration.Reload();
|
|
442
|
+
```
|