ZKWeb实现了一个非常灵活和强大的插件系统,
支持在任何位置存放插件, 在代码修改后自动编译, 以及发布时可以只发布程序集等等.
ZKWeb对插件的管理不依赖于IDE, 内部会调用Roslyn的API来完成对插件源代码的编译.
插件的查找和加载
在前面的配置文件已经提到过, 插件列表保存在App_Data\config.json
下.
加载时会按Plugins
中定义的顺序进行加载, 然后按PluginDirectories
中定义的顺序进行查找.
以上面的配置为例, 假定网站项目在D:\Projects\Hello.World\src\Hello.World.AspNetCore
,
会查找以下的路径加载Common.Base
这个插件:
D:\Projects\ZKWeb.Plugins\Common.Base D:\Projects\Hello.World\src\Hello.World.Plugins\Common.Base
加载完Common.Base
后会按查找以下的路径加载Common.Captcha
:
D:\Projects\ZKWeb.Plugins\Common.Captcha D:\Projects\Hello.World\src\Hello.World.Plugins\Common.Captcha
一直到Plugins
中的插件全部加载完毕.
插件信息
各个插件都会有文件和版本等信息, 这些信息保存在插件文件夹下的plugin.json
中, 格式如下:
{ "Name": "插件名称", "Version": "插件版本", "Description": "插件描述", "Dependencies": [ "依赖的其他插件" ], "References": [ "依赖的程序集" ] }
以下是Common.Admin
插件的plugin.json
:
{ "Name": "Admin Panel", "Version": "1.8.0", "Description": "Admin panel and users management", "Dependencies": [ "Common.Base", "Commin.Captcha" ] }
添加新的插件时仿照这个格式编写plugin.json
即可.
插件的目录结构
插件的目录结构在网站结构中已经提过, 如下:
- 插件名称
- bin 由插件编译出来的程序集
- src 插件的源代码
- Controllers 储存控制器的文件夹
- 更多保存代码的文件夹...
- Plugin.cs 载入插件时的处理, 可以省略
- static 静态文件
- templates 模板文件
- templates.mobile 移动端专用的模板文件
- plugin.json 插件信息
使用发布工具发布网站后会自动删除src目录, 所以最终发布出来的插件不会包含源代码.
依赖的程序集
插件需要依赖外部的程序集时, 需要在plugin.json
的References
指定.
以Common.QRCoder
为例
- Common.QRCoder 插件文件夹
- references
- net
- QRCoder.dll net461版本的程序集
- netstandard
- QRCoder.dll netstandard版本的程序集
- net
- plugin.json
- references
plugin.json
的内容如下
{ "Name": "QRCoder", "Version": "1.8.0", "Description": "Support generate QRCode", "References": [ "QRCoder" ], "Dependencies": [ "Common.Base" ] }
ZKWeb在读取到References
后会自动从references
文件夹下查找对应的程序集并载入.
透过式文件系统
ZKWeb使用了类似Django的透过式文件系统, 一个插件可以简单的重载另外一个插件的资源文件.
读取资源文件的顺序如下, 会返回最先存在的路径.
"App_Data/路径" foreach (按加载顺序的相反顺序枚举插件) { "插件目录/路径" }
例如插件加载顺序是 Plugins: [ "PluginA", "PluginB" ]
, 且目录结构如下:
读取资源templates/some_folder/some.html
会读取PluginB
下的文件,
读取资源static/other_folder/other.txt
会读取PluginA
下的文件.
读取资源文件可以使用以下的代码
var fileStorage = Application.Ioc.Resolve<IFileStorage>(); var file = fileStorage.GetResourceFile("templates", "some_folder", "some.html"); var contents = file.ReadAllText();
组件的注册顺序
在ZKWeb中, 插件的注册顺序会影响组件的注册顺序.
例如插件加载顺序是 Plugins: [ "PluginA", "PluginB" ]
,
插件PluginA
有[ExportMany]class ExampleHandlerA : IExampleHandler { }
,
插件PluginB
有[ExportMany]class ExampleHandlerB : IExampleHandler { }
.
这时使用Application.Ioc.ResolveMany<IExampleHandler>()
会获取到包含两个实例的列表,
第一个实例类型是ExampleHandlerA, 第二个实例类型是ExampleHandlerB.
在网站启动时执行操作
部分插件可能需要在网站启动时执行一些操作, 可以使用IPlugin
接口:
[ExportMany] public Plugin : IPlugin { public Plugin() { // 这里会在网站启动时运行 // 并且会按插件的注册顺序运行 } }
调试插件
调试在当前项目内的插件很简单, 直接F5运行并下断点即可.
调试在当前项目外的插件有两种办法:
- 第一种
- 在插件的VS中选择
调试-挂载到进程
并选择IIS进程或Kestrel进程挂载 - Asp.Net, Owin选择
iisexpress.exe
- Asp.Net Core选择
项目主程序.exe
, 注意不要选iis
或者w3wp
- 在插件的VS中选择
- 第二种
- 把需要调试的文件拖动到Visual Studio中,然后在里面下断点
禁止检测插件源代码更新
在默认情况下, ZKWeb会自动检测插件源代码的更新, 更新时会自动重启服务器以重新编译和加载插件.
如果你不想ZKWeb自动重启服务器可以添加以下的选项到App_Data\config.json
:
{ "Extra": { "ZKWeb.DisableAutomaticPluginReloading": true } }