上一篇博客中,讲到了将WebApi Host到控制台和IIS,本篇总结一下如何将WebApi的Service以插件的形式进行动态部署,并设置Hoster的首页显示Api帮助文档,当然,也包括动态部署进来的插件的Api帮助。
话不多说,上酸菜,啊不, 上干货。
- 建立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 确保没有错误
- 建立类库项目若干
右键点击解决方案, 新建类库,命名为 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 项。
如法炮制,可以多建立几个类似的项目供测试。
- 测试
发布或者直接使用IISExpress运行(确保插件目录已存在,并将步骤2中的类库生成的Dll文件和Xml文件拷贝至插件目录)
测试结果如下: