[2017-08-21]Abp系列——如何使用Abp插件機制(注冊權限、菜單、路由)



本系列目錄:Abp介紹和經驗分享-目錄

Abp的模塊系統支持插件機制,可以在指定目錄中放置模塊程序集,然后應用程序啟動時會搜索該目錄,加載其中所有程序集中的模塊。

如何使用這套機制進行功能插件化開發?

首先,插件程序集和應用程序是毫無關系的,應用程序不依賴這個程序集,所以我們要解決幾個常見問題:

  1. 插件中提供的功能需要權限認證,如何自動注冊權限到使用了該插件的應用程序?
  2. 插件中提供的功能在展現層中需要菜單導航,如何自動注冊菜單項目?
  3. 插件中提供的功能需要配置,如何讓插件自已能進行簡單的配置管理,而不用去改宿主的配置文件?
  4. 插件中提供了新的Mvc Controller,當然也需要注冊路由。

以下代碼從本系列QuickStartA中的Personball.Demo解決方案開始

開始我們的第一個插件程序集的開發

首先啟動QuickStartA中一切就緒的HelloWorld,並且登陸進去看看首頁

abp_demohome

一切正常!

Step1 新建插件程序集

abp_plugin_start
  1. 在解決方案中新建目錄PlugIns,新建程序集項目Personball.PlugIns.PlugInZero
  2. 修改默認命名空間,由於這是一個插件,我選擇默認命名空間為Personball.PlugIns,將PlugInZero作為這個示例插件的名稱;
  3. 在程序包管理其控制台,選擇默認項目PlugIns\Personball.PlugIns.PlugInZero,執行Install-Package Abp.Web.Mvc -Version 2.3(保證和宿主使用的Abp框架版本一致,可以減少很多不必要的麻煩,這里安裝Abp.Web.Mvc是因為我們將在插件中實現一個MvcController);
  4. 在插件程序集中添加一個目錄PlugInZero,移除默認的Class1.cs。

Step2 注冊權限

Abp中權限構建基本都是通過繼承AuthorizationProvider,實現SetPermissions方法,並添加到IAuthorizationConfiguration.Providers
插件本身也是一個模塊,只要實現自己的AuthorizationProvider,並注冊進Providers即可。
PlugInZero目錄中定義我們的插件模塊PlugInZeroModule,代碼如下:
因本博客樣式原因,源代碼排版容易亂,下文大段代碼全部貼圖,文末附最終代碼壓縮包供下載。

abp_plugin_module

PlugInZero目錄下新建常量定義文件PlugInZeroConsts,代碼如下:

namespace Personball.PlugIns.PlugInZero
{
    public static class PlugInZeroConsts
    {
        public static class PermissionNames
        {
            public const string PlugIns = "Personball.PlugIns";
            public const string PlugInZero = "Personball.PlugIns.PlugInZero";
        }
    }
}

PlugInZero目錄下新建目錄Authorization,新建類PlugInZeroAuthorizationProvider,代碼如下:

abp_plugin_auth_provider

再到插件模塊PlugInZeroModule中注冊上述權限:

public override void PreInitialize()
{
    Configuration.Authorization.Providers
        .Add<PlugInZeroAuthorizationProvider>();
    //TODO 等會要用
}

Step3 等不及要先看看效果了,讓我們啟用Personball.Demo.Web的插件加載!

讓我們打開Personball.Demo.Web項目的Global.asax文件,添加代碼后如下:

abp_plugin_web_global
  1. Personball.Demo.Web項目添加一個目錄PlugIns;
  2. 生成插件程序集,將插件項目bin\debug目錄中的Personball.PlugIns.PlugInZero.dllPersonball.PlugIns.PlugInZero.pdb復制到Personball.Demo.WebPlugIns目錄下,啟動Personball.Demo.Web!
  3. 啟動后,登陸,點開Roles菜單,點開角色Admin的編輯彈窗,看權限的選項,確實增加了我們剛才在插件中定義的新權限!
abp_plugin_permission

如果你有Asp.Net Zero的代碼(收費的),那么權限編輯功能是可以直接使用的,這里官網免費生成的項目僅包含了module-zero基礎功能,UI部分並未實現權限編輯功能。

Step4 注冊菜單

和權限類似,菜單通過繼承NavigationProvider,實現SetNavigation方法,並添加到INavigationConfiguration.Providers
同上,插件可以實現自己的NavigationProvider,並注冊。

PlugInZero目錄下新建目錄Navigation,新建類PlugInZeroNavigationProvider,代碼如下:

abp_plugin_menu_code

再到插件模塊PlugInZeroModule中注冊菜單:

public override void PreInitialize()
{
    //注冊權限
    Configuration.Authorization.Providers
        .Add<PlugInZeroAuthorizationProvider>();
    //注冊菜單
    Configuration.Navigation.Providers
        .Add<Navigation.PlugInZeroNavigationProvider>();
}

來看看效果(生成插件程序集,並復制替換到Web項目的PlugIns目錄下):

abp_plugin_menu

權限和菜單相對簡單,接下來是重點。

Step5 注冊路由和尋找Controller

首先我們提供一個PlugInZeroController,簡單起見,僅有一個返回字符串的Action。
PlugInZero目錄下新建目錄Controller,新建類PlugInZeroController,代碼如下:

public class PlugInZeroController : AbpController
{
    public PlugInZeroController()
    {
        LocalizationSourceName = "Abp";
    }

    public Task<string> Hello()
    {
        return Task.FromResult($"hello at {DateTime.Now}");
    }
}

接着,我們注冊下路由(在PlugInZeroModule的PreInitialize方法中):

//注冊權限
Configuration.Authorization.Providers
    .Add<PlugInZeroAuthorizationProvider>();
//注冊菜單
Configuration.Navigation.Providers
    .Add<Navigation.PlugInZeroNavigationProvider>();
//注冊路由
RouteTable.Routes.MapRoute(
    "Plugins",
    url: "PlugIns/{controller}/{action}",
    defaults: new { controller = "Home", action = "Index" });

修改一下菜單項上的url,並指定鏈接的target(在PlugInZeroNavigationProvider中):

plugInRoot.AddItem(
        new MenuItemDefinition(
            "PlugInZero",
            new FixedLocalizableString("PlugInZero(插件)"),
            //指定Controller的url
            url: "PlugIns/PlugInZero/Hello",
            icon: "",
            target: "_blank"//新開一個窗口
            ));

更新插件程序集,啟動,點擊之前的插件菜單(如果遇到404,請參考下方Tip01):

abp_plugin_mvc

Step6 插件配置

OK,Last Question:如何提供插件單獨的配置?
思路是,從程序集的App.config入手!
如果插件自身使用數據庫,有個DbContext,怎么讀取配置並讓IoC構建DbContext時使用這個配置?

右鍵插件項目,新增項目,選擇應用程序配置文件,文件名自動就是App.config,添加!
編輯App.config,如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
    <add name="PlugInZeroDB" connectionString="localhost" providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
    <add key="PlugInZeroSettingKey" value="Wahoo, wahoo"/>
</appSettings>
</configuration>

改下PlugInZeroController的代碼嘗試讀取PlugInZeroSettingKey並輸出:

public Task<string> Hello()
{
    var config = ConfigurationManager.OpenExeConfiguration(
        Assembly.GetExecutingAssembly().Location);
    var value = config.AppSettings
        .Settings["PlugInZeroSettingKey"].Value;
    return Task.FromResult($"hello at {DateTime.Now}  {value}");
}

重新生成插件程序集,並復制三個文件(含Personball.PlugIns.PlugInZero.dll.config)到PlugIns目錄中,啟動:

abp_plugin_config

讀取數據庫連接字符串,並讓指定的DbContext(插件自己的)使用該配置,這里僅給出示例代碼,並不實際運行演示。

//插件配置(直接從當前執行的程序集的config文件讀取數據庫連接串)
var config = ConfigurationManager.OpenExeConfiguration(
    Assembly.GetExecutingAssembly().Location);
string connectStr = config.ConnectionStrings.ConnectionStrings["PlugInZeroDB"].ConnectionString;
//注冊DbContext,構建時使用指定參數
IocManager.IocContainer.Register(
    Component.For<PlugInZeroDbContext>()
    .DependsOn(
        Dependency.OnValue(
            "connectionString", connectStr)));

Tip01

注意!注意!注意!
BuildManager對於ControllerType有緩存,在服務器上僅僅加載插件,注冊路由,可能還是會遇到404(找不到Controller)。
這種時候必須改動對於iis敏感的幾個路徑(bin目錄)或Web.config文件,BuildManager才會更新ControllerType的緩存(這是個文件緩存!),將插件內的Controller類型也算進去。
這里雖然只有寥寥數語,卻是各種心酸血淚之后的總結,期間甚至自己擴展過一個ControllerFactory,那也是一套可行的方案,不贅述了。

搜了一個MVC-ControllerTypeCache.xml,內容如下(這里並未包含插件中的PlugInZeroController):

<?xml version="1.0" encoding="utf-8"?>
<!--This file is automatically generated. Please do not modify the contents of this file.-->
<typeCache lastModified="8/22/2017 12:00:44 AM" mvcVersionId="cc73190b-ab9d-435c-8315-10ff295c572a">
<assembly name="Personball.Demo.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <module versionId="9c27c37d-a073-42aa-b339-b5887549b123">
    <type>Personball.Demo.Web.Controllers.AboutController</type>
    <type>Personball.Demo.Web.Controllers.AccountController</type>
    <type>Personball.Demo.Web.Controllers.HomeController</type>
    <type>Personball.Demo.Web.Controllers.RolesController</type>
    <type>Personball.Demo.Web.Controllers.TenantsController</type>
    <type>Personball.Demo.Web.Controllers.UsersController</type>
    <type>Personball.Demo.Web.Controllers.LayoutController</type>
    </module>
</assembly>
<assembly name="Abp.Web.Mvc, Version=2.3.0.0, Culture=neutral, PublicKeyToken=null">
    <module versionId="6bcf306f-dc82-4ad6-99be-7efe88288f89">
    <type>Abp.Web.Mvc.Controllers.AbpAppViewController</type>
    <type>Abp.Web.Mvc.Controllers.AbpScriptsController</type>
    <type>Abp.Web.Mvc.Controllers.AbpUserConfigurationController</type>
    <type>Abp.Web.Mvc.Controllers.Localization.AbpLocalizationController</type>
    </module>
</assembly>
</typeCache>

Tip02

插件的dll文件替換時遇到進程鎖定問題
請先停止iis站點或者應用程序池,替換插件dll后再啟動

如果配置了CI,比如tfs使用webdeploy發布
務必請在發布時指定webdeploy的選項,忽略插件目錄-skip:Directory="PlugIns"
插件一般考慮手動更新(大多是非核心的功能,變更極少),如果CI每次都要考慮重新build插件並更新,就得不償失了。
假如非要CI每次更新插件程序集,那就需要先用webdeploy停止目標站點的應用程序池,發完后再啟動應用程序池。
詳情請參見下方參考條目:Operations on application pools as admin and non-admin

參考

ABP模塊系統插件機制

Taming the BuildManager, ASP.Net Temp files and AppDomain restarts

Operations on application pools as admin and non-admin

本文源碼下載

Personball.Demo.PlugIn.7z


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM