插件式WebApi服务及自动生成Api帮助文档

上一篇博客中,讲到了将WebApi Host到控制台和IIS,本篇总结一下如何将WebApi的Service以插件的形式进行动态部署,并设置Hoster的首页显示Api帮助文档,当然,也包括动态部署进来的插件的Api帮助。


话不多说,上酸菜,啊不, 上干货。

源码地址

  1. 建立Host
    新建 Asp.Net Web Application, 解决方案名称填写WeiApiPluginDemo,项目名称填写IISHost,确定

选择 Empty, 确定

菜单中点选 Tools -> NuGet Package Manager -> Package Manager Console
控制台中输入

Install-Package Microsoft.AspNet.WebApi.HelpPage

将会在IISHost项目中增加Area目录以及目录下的一大坨目录和文件,暂且放下不管,等会我们再来配置和编码。

鼠标右键点击IISHost项目,Add -> Global Application Class, 建立全局配置文件, 在弹出的 Specify Name for Item 窗体中点击 OK, 使用默认的 Item name

Global.asax.cs 中, 删掉除 Application_Start 外的其他方法(这里用不到)

添加代码如下:

      protected void Application_Start(object sender, EventArgs e)
      {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
      }    
 鼠标右键点击IISHost项目,*Add -> New Folder*, 建立名为 *App_Start* 的目录,右键点击新建的目录,*Add->Class*, 新建名为 *RouteConfig.cs* 的类.

 最终代码如下, 注意添加相关引用。
 ```csharp
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}
 ```

 鼠标右键点击 *App_Start* 目录,*Add->Class*, 新建名为 *WebApiConfig.cs* 的类,添加 *Register* 方法
 ```csharp
    public static void Register(HttpConfiguration config)
    {
        config.Services.Replace(typeof(IAssembliesResolver), new PluginsAssembliesResolver());

        config.MapHttpAttributeRoutes();

        config.EnableCors();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
         );
    }
 ```

在项目根目录中新建一个名为 WebSettingsConfig 的辅助类

    public class WebSettingsConfig
    {
        public static string plugin_location
        {
            get
            {
                return AppSettingValue();
            }
        }

        private static string AppSettingValue([CallerMemberName] string key = null)
        {
            return ConfigurationManager.AppSettings[key];
        }
    }

web.config 配置, 确保 bin/plugins 目录存在,这个目录是将来我们存放插件的地方

   <configuration>
     <appSettings>
     <!--插件目录-->
     <add key="plugin_location" value="~/bin/plugins"/>
     </appSettings>
     ...

回到 Global.asax.cs, 添加对 RouteConfig 的命名空间引用

右键点击 App_Start 目录,新建名为 PluginsAssembliesResolver 的类,该类负责解析插件(dll)

代码如下:

      ```csharp
    /// <summary>
    /// 发现非引用Dll中的Controller,意即插件
    /// </summary>
    public class PluginsAssembliesResolver : DefaultAssembliesResolver
    {
        public virtual ICollection<Assembly> GetAssemblies()
        {
            ICollection<Assembly> baseAssemblies = base.GetAssemblies();
            var mp = HttpContext.Current.Server.MapPath(WebSettingsConfig.plugin_location);

            foreach (string file in Directory.GetFiles(mp, "*.dll"))
            {
                var controllersAssembly = Assembly.LoadFile(file);
                baseAssemblies.Add(controllersAssembly);
            }

            List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
            return assemblies;

        }
    }

鼠标右键点击 IISHost 项目,Add -> New Folder, 建立名为 Controllers 的目录,右键点击新建的目录,Add -> Controller...,在弹出的 Add Scaffold 窗体中选择 MVC5 Controller * Empty,点击 Add,在 Add Controller 窗体将新的 Controller 命名为 HomeController

替换 HomeController.cs 中的 Index() 内容,最后代码长这样

     public class HomeController : Controller
     {
        // GET: Home
        public ActionResult Index()
        {
            return RedirectToAction("Index", "Help"); //替换原来的 return View();
        }
     }

删掉系统自动创建的 Views 目录

打开 IISHost 项目中的 Areas->HelpPage->App_Start->HelpPageConfig.cs, 在 Register 方法中添加如下代码

     public static void Register(HttpConfiguration config)
     {
        //添加
        config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath(WebSettingsConfig.plugin_location)));
     }

HelpPageConfig.cs 的上一级目录下(也就是HelpPage目录下)新建名为 MultiXmlDocumentationProvider.cs 的 class

该类实现 IDocumentationProvider, IModelDocumentationProvider 两个接口,具体代码如下:

    public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
    {
        //说白了就是查找目录下的所有Xml文档,加载到 _documentationProviders 中
        private IList<XmlDocumentationProvider> _documentationProviders;

        public MultiXmlDocumentationProvider(string xmlDocFilesPath)
        {
            _documentationProviders = new List<XmlDocumentationProvider>();

            foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
            {
                _documentationProviders.Add(new XmlDocumentationProvider(file));
            }
        }

        public string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
        {
            return _documentationProviders.Select(x => x.GetDocumentation(parameterDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
        }

        public string GetDocumentation(Type type)
        {
            return _documentationProviders.Select(x => x.GetDocumentation(type)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
        }

        //成员导航
        public string GetDocumentation(MemberInfo member)
        {
            return _documentationProviders
          .Select(x => x.GetDocumentation(member))
          .FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
        }

        //action 描述
        public string GetDocumentation(HttpActionDescriptor actionDescriptor)
        {
            return _documentationProviders.Select(x => x.GetDocumentation(actionDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
        }

        //Controller 描述
        public string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
        {
            return _documentationProviders.Select(x => x.GetDocumentation(controllerDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
        }

        public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
        {
            return _documentationProviders.Select(x => x.GetDocumentation(actionDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));

        }
    }

编译项目 IISHost 确保没有错误

  1. 建立类库项目若干

右键点击解决方案, 新建类库,命名为 DemoClass01

菜单中点选 Tools -> NuGet Package Manager -> Package Manager Console
控制台中输入

    Install-Package Microsoft.AspNet.WebApi.Core

在项目 DemoClass01 中新建目录 Controllers 并新建 Controllers\DemoClass01.cs, 代码如下:

    /// <summary>
    /// 测试DemoClass01
    /// </summary>
    [RoutePrefix("demo")]
    public class DemoController : ApiController
    {
        /// <summary>
        /// 测试方法1
        /// </summary>
        /// <returns></returns>
        [HttpGet, Route("get1")]
        public HttpResponseMessage Get()
        {
            return Request.CreateResponse("demo class 01 response");
        }
    }    

右键单击项目 Properties->Build 勾选 XML documentation file 项。

如法炮制,可以多建立几个类似的项目供测试。

  1. 测试

发布或者直接使用IISExpress运行(确保插件目录已存在,并将步骤2中的类库生成的Dll文件和Xml文件拷贝至插件目录)

测试结果如下:

源码地址

posted @ 2017-02-08 15:37  DebugLife  阅读(615)  评论(0编辑  收藏  举报