什么是ZKEACMS for .Net Core?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
ZKEACMS 簡介
ZKEACMS.Core 是基于 .Net Core MVC 開發的開源CMS。ZKEACMS可以讓用戶自由規劃頁面布局,使用可視化編輯設計“所見即所得”,直接在頁面上進行拖放添加內容。
ZKEACMS使用插件式設計,模塊分離,通過橫向擴展來豐富CMS的功能。
響應式設計
ZKEACMS使用Bootstrap3的柵格系統來實現響應式設計,從而實現在不同的設備上都可以正常訪問。同時站在Bootstrap巨人的肩膀上,有豐富的主題資源可以使用。
簡單演示

接下來看看程序設計及原理
項目結構
EasyFrameWork 底層框架
ZKEACMS CMS核心
ZKEACMS.Article 文章插件
ZKEACMS.Product 產品插件
ZKEACMS.SectionWidget 模板組件插件
ZKEACMS.WebHost
原理 - 訪問請求流程
路由在ZKEACMS里面起到了關鍵性的作用,通過路由的優先級來決定訪問的流程走向,如果找到匹配的路由,則優先走該路由對應的 Controller -> Action -> View,如果沒有匹配的路由,則走路由優先權最低的“全捕捉”路由來處理用戶的請求,最后返回響應。
優先級最低的“全捕捉”路由是用來處理用戶自行創建的頁面的。當請求進來時,先去數據庫中查找是否存在該頁面,不存在則返回404。找到頁面之后,再找出這個頁面所有的組件、內容,然后統一調用各個組件的“Display"方法來來得到對應的“ViewModel"和視圖"View",最后按照頁面的布局來顯示。
ZKEACMS 請求流程圖

驅動頁面組件:
widgetService.GetAllByPage(filterContext.HttpContext.RequestServices, page).Each(widget =>
{
if (widget != null)
{
IWidgetPartDriver partDriver = widget.CreateServiceInstance(filterContext.HttpContext.RequestServices);
WidgetViewModelPart part = partDriver.Display(widget, filterContext);
lock (layout.ZoneWidgets)
{
if (layout.ZoneWidgets.ContainsKey(part.Widget.ZoneID))
{
layout.ZoneWidgets[part.Widget.ZoneID].TryAdd(part);
}
else
{
layout.ZoneWidgets.Add(part.Widget.ZoneID, new WidgetCollection { part });
}
}
partDriver.Dispose();
}
});頁面呈現:
foreach (var widgetPart in Model.ZoneWidgets[zoneId].OrderBy(m => m.Widget.Position).ThenBy(m => m.Widget.WidgetName))
{
<p style="@widgetPart.Widget.CustomStyle">
<p class="widget @widgetPart.Widget.CustomClass">
@if (widgetPart.Widget.Title.IsNotNullAndWhiteSpace())
{
<p class="panel panel-default">
<p class="panel-heading">
@widgetPart.Widget.Title
</p>
<p class="panel-body">
@Html.DisPlayWidget(widgetPart)
</p>
</p>
}
else
{
@Html.DisPlayWidget(widgetPart)
}
</p>
</p>
}插件“最關鍵”的類 PluginBase
每一個插件/模塊都必需要一個類繼承PluginBase,作為插件初始化的入口,程序在啟動的時候,會加載這些類并作一些關鍵的初始化工作。
public abstract class PluginBase : ResourceManager, IRouteRegister, IPluginStartup
{
public abstract IEnumerable<RouteDescriptor> RegistRoute(); //注冊該插件所需要的路由 可返回空
public abstract IEnumerable<AdminMenu> AdminMenu(); //插件在后端提供的菜單 可返回空
public abstract IEnumerable<PermissionDescriptor> RegistPermission(); //注冊插件的權限
public abstract IEnumerable<Type> WidgetServiceTypes(); //返回該插件中提供的所有組件的類型
public abstract void ConfigureServices(IServiceCollection serviceCollection); //IOC 注冊對應的接口與實現
public virtual void InitPlug(); //初始化插件,在程序啟動時調用該方法
}具體實現可以參考“文章”插件 ArticlePlug.cs 或者“產品”插件 ProductPlug.cs
加載插件 Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.UseEasyFrameWork(Configuration).LoadEnablePlugins(plugin =>
{
var cmsPlugin = plugin as PluginBase;
if (cmsPlugin != null)
{
cmsPlugin.InitPlug();
}
}, null);
}組件構成
一個頁面,由許多的組件構成,每個組件都可以包含不同的內容(Content),像文字,圖片,視頻等,內容由組件決定,呈現方式由組件的模板(View)決定。
關系與呈現方式大致如下圖所示:

實體 Enity
每個組件都會對應一個實體,用于存儲與該組件相關的一些信息。實體必需繼承于 BasicWidget 類。
例如HTML組件的實體類:
[ViewConfigure(typeof(HtmlWidgetMetaData)), Table("HtmlWidget")]
public class HtmlWidget : BasicWidget
{
public string HTML { get; set; }
}
class HtmlWidgetMetaData : WidgetMetaData<HtmlWidget>
{
protected override void ViewConfigure()
{
base.ViewConfigure();
ViewConfig(m => m.HTML).AsTextArea().AddClass("html").Order(NextOrder());
}
}實體類里面使用到了元數據配置[ViewConfigure(typeof(HtmlWidgetMetaData))],通過簡單的設置來控制表單頁面、列表頁面的顯示。假如設置為文本或下拉框;必填,長度等的驗證。
這里實現方式是向MVC里面添加一個新的ModelMetadataDetailsProviderProvider,這個Provider的作用就是抓取這些元數據的配置信息并提交給MVC。
services.AddMvc(option =>
{
option.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider());
})服務 Service
WidgetService 是數據與模板的橋梁,通過Service抓取數據并送給頁面模板。 Service 必需繼承自 WidgetService<WidgetBase, CMSDbContext>。如果業務復雜,則重寫(override)基類的對應方法來實現。
例如HTML組件的Service:
public class HtmlWidgetService : WidgetService<HtmlWidget, CMSDbContext>
{
public HtmlWidgetService(IWidgetBasePartService widgetService, IApplicationContext applicationContext)
: base(widgetService, applicationContext)
{
}
public override DbSet<HtmlWidget> CurrentDbSet
{
get
{
return DbContext.HtmlWidget;
}
}
}視圖實體 ViewModel
ViewModel 不是必需的,當實體(Entity)作為ViewModel傳到視圖不足以滿足要求時,可以新建一個ViewModel,并將這個ViewModel傳過去,這將要求重寫 Display 方法
public override WidgetViewModelPart Display(WidgetBase widget, ActionContext actionContext)
{
//do some thing
return widget.ToWidgetViewModelPart(new ViewModel());
}視圖 / 模板 Widget.cshtml
模板 (Template) 用于顯示內容。通過了Service收集到了模板所要的“Model”,最后模板把它們顯示出來。
動態編譯分散的模板
插件的資源都在各自的文件夾下面,默認的視圖引擎(ViewEngine)并不能找到這些視圖并進行編譯。MVC4版本的ZKEACMS是通過重寫了ViewEngine來得以實現。.net core mvc 可以更方便實現了,實現自己的 ConfigureOptions<RazorViewEngineOptions> ,然后通過依賴注入就行。
public class PluginRazorViewEngineOptionsSetup : ConfigureOptions<RazorViewEngineOptions>
{
public PluginRazorViewEngineOptionsSetup(IHostingEnvironment hostingEnvironment, IPluginLoader loader) :
base(options => ConfigureRazor(options, hostingEnvironment, loader))
{
}
private static void ConfigureRazor(RazorViewEngineOptions options, IHostingEnvironment hostingEnvironment, IPluginLoader loader)
{
if (hostingEnvironment.IsDevelopment())
{
options.FileProviders.Add(new DeveloperViewFileProvider());
}
loader.GetPluginAssemblies().Each(assembly =>
{
var reference = MetadataReference.CreateFromFile(assembly.Location);
options.AdditionalCompilationReferences.Add(reference);
});
loader.GetPlugins().Where(m => m.Enable && m.ID.IsNotNullAndWhiteSpace()).Each(m =>
{
var directory = new DirectoryInfo(m.RelativePath);
if (hostingEnvironment.IsDevelopment())
{
options.ViewLocationFormats.Add($"/Porject.RootPath/{directory.Name}" + "/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"/Porject.RootPath/{directory.Name}" + "/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"/Porject.RootPath/{directory.Name}" + "/Views/{0}" + RazorViewEngine.ViewExtension);
}
else
{
options.ViewLocationFormats.Add($"/{Loader.PluginFolder}/{directory.Name}" + "/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"/{Loader.PluginFolder}/{directory.Name}" + "/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"/{Loader.PluginFolder}/{directory.Name}" + "/Views/{0}" + RazorViewEngine.ViewExtension);
}
});
options.ViewLocationFormats.Add("/Views/{0}" + RazorViewEngine.ViewExtension);
}
}看上面代碼您可能會產生疑惑,為什么要分開發環境。這是因為ZKEACMS發布和開發的時候的文件夾目錄結構不同造成的。為了方便開發,所以加入了開發環境的特別處理。接下來就是注入這個配置:
services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<RazorViewEngineOptions>, PluginRazorViewEngineOptionsSetup>());
EntityFrameWork
ZKEACMS for .net core 使用EntityFrameWork作為數據庫訪問。數據庫相關配置 EntityFrameWorkConfigure
public class EntityFrameWorkConfigure : IOnConfiguring
{
public void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(Easy.Builder.Configuration.GetSection("ConnectionStrings")["DefaultConnection"]);
}
}對Entity的配置依然可以直接寫在對應的類或屬性上。如果想使用 Entity Framework Fluent API,那么請創建一個類,并繼承自 IOnModelCreating
class EntityFrameWorkModelCreating : IOnModelCreating
{
public void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<LayoutHtml>().Ignore(m => m.Description).Ignore(m => m.Status).Ignore(m => m.Title);
}
}主題
ZKEACMS 使用Bootstrap3作為基礎,使用LESS,定議了許多的變量,像邊距,顏色,背景等等,可以通過簡單的修改變量就能“編譯”出一個自己的主題。
或者也可以直接使用已經有的Bootstrap3的主題作為基礎,然后快速創建主題。
關于什么是ZKEACMS for .Net Core問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。