「ASP.NET Web API」自定义的宿主环境中使用外部程独立 Controller
原文链接 http://inskyline.com/net/2015/12/04/a-net-asp.net_WebApiuser_defined_selfhostServer.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
背景
最近打算对一个 Web API 项目做代码混淆,但由于宿主环境是 IIS
,导致完全混淆后, IIS 不能很好的解析。于是决定自己写一个宿主环境。
用一个控制台项目,用一段简单的代码
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://127.0.0.1:3333");
config.Routes.MapHttpRoute("default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
var server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
Console.WriteLine("Server is opened");
Console.Read();
}
将外部的 Controller dll
以及相关的 dll 引入到当前的程序下。打开浏览器请求。
出现以下错误
<Error>
<Message>
未找到与请求 URI“http://127.0.0.1:3333/api/Product/Values”匹配的 HTTP 资源。
</Message>
<MessageDetail>未找到与名为“Product”的控制器匹配的类型。</MessageDetail>
</Error>
经分析导致错误的原因是:默认注册的 DefaultAssembliesResolver
仅仅提供当前应用程序域加载的程序集。我引入的程序集并没有加载在当前程序域中。
于是先继承 DefaultAssembliesResolver
将独立的 dll 添加到当前程序域中。
代码如下:
public class CustomAssembliesResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
ICollection<Assembly> baseAssemblies = base.GetAssemblies();
List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
var controllersAssembly = Assembly.LoadFrom(@"TestHostApi.dll");
baseAssemblies.Add(controllersAssembly);
return baseAssemblies;
}
}
对宿主程序做一小点修改为:
var config = new HttpSelfHostConfiguration("http://127.0.0.1:3333");
config.Routes.MapHttpRoute("default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
var server = new HttpSelfHostServer(config);
CustomAssembliesResolver assemblyResolver = new CustomAssembliesResolver();
server.Configuration.Services.Replace(typeof(IAssembliesResolver), new ExtendedDefaultAssembliesResolver());
server.OpenAsync().Wait();
Console.WriteLine("Server is opened");
Console.Read();
这样再次打开浏览器访问,得到了我想要的结果。
追溯
问题解决了,但 HttpSelfHostServer
到底是如何工作的,不妨追根寻源,探究以它本后的逻辑。
继承体系
├─System.Object
│ ├─HttpMessageHandler
│ └─DelegatingHandler
│ └─System.Web.Http.HttpServer
└─System.Web.Http.SelfHost.HttpSelfHostServer
HttpMessageHandler
ASP.NET Web 核心框架是基于消息的管道。所有的消息都经过 HttpMessageHandler
处理,这个独立的抽象管道独立于寄宿环境。
代码:
public abstract class HttpMessageHandler : IDisposable
{
public void Dispose();
protected virtual void Dispose(bool disposing);
protected abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
}
DelegatingHandler
ASP.NET Web API 消息处理管道是通过一组有序的 HttpMessagHandler
『首尾相连』而成,具体实现是通过 DelegatingHandler
这个类型来完成的。
代码:
public abstract class DelegatingHandler : HttpMessageHandler
{
protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
public HttpMessageHandler InnerHandler get; set; }
}
DelegatingHandler
根据其 InnerHandler
获得被委托的 HttpMessageHandler
对象的引用,对消息处理。
HttpServer
HttpServer
直接继承自 DelegatingHandler
。它有两个重要的只读属性(Configuration
和 Dispatcher
),前者得到用于配置整个消息处理管道的 HttpConfiguration
对象,另外一个属性Dispatcher
返回的是处于整个消息处理管道『尾端』的HttpMessageHandler
。
代码:
public class HttpServer : DelegatingHandler
{
public HttpConfiguration Configuration { get; }
public HttpMessageHandler Dispatcher { get; }
public HttpServer();
public HttpServer(HttpMessageHandler dispatcher);
public HttpServer(HttpConfiguration configuration);
public HttpServer(HttpConfiguration configuration, HttpMessageHandler dispatcher);
protected override void Dispose(bool disposing);
protected virtual void Initialize();
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
}
HttpServer
的 Configuration
和 Dispatcher
属性均可以在相应的构造函数中初始化。如果在构造HttpServer
的时候没有显式指定这两个属性的值(调用默认的无参构造函数创建 HttpServer
),在默认情况下会创建一个HttpConfiguration
作为Configuration
的属性值,而作为Dispatcher
属性值的则是一个HttpRoutingDispatcher
对象,该类型定义在命名空间System.Web.Http.Dispatcher
下。
整个管道都是由HttpConfiguration
进行配置:
HttpConfiguration
代码
public class HttpConfiguration : IDisposable
{
public HttpConfiguration();
public HttpConfiguration(HttpRouteCollection routes);
// 获取或设置与此实例关联的依赖关系解析程序。
//返回结果:依赖关系解析程序。
public IDependencyResolver DependencyResolver { get; set; }
//筛选器列表。
public HttpFilterCollection Filters { get; }
public MediaTypeFormatterCollection Formatters { get; }
public IncludeErrorDetailPolicy IncludeErrorDetailPolicy { get; set; }
public Action<HttpConfiguration> Initializer { get; set; }
public Collection<DelegatingHandler> MessageHandlers { get; }
public ParameterBindingRulesCollection ParameterBindingRules { get; internal set; }
public ConcurrentDictionary<object, object> Properties { get; }
public HttpRouteCollection Routes { get; }
//获取与此实例关联的默认服务的容器。
// 返回结果:
// 包含此实例的默认服务的 System.Web.Http.Controllers.ServicesContainer。
public ServicesContainer Services { get; internal set; }
public string VirtualPathRoot { get; }
public void Dispose();
protected virtual void Dispose(bool disposing);
}
其中属性 DependencyResolver
和 Services
提供了很大的灵活性。
HttpSelfHostServer
HttpSelfHostServer
是一个密封类,继承自HttpServer
。它的构造中用了 Configuration
的派生类(HttpSelfHostConfiguration
),并提供监听的打开和关闭方法。
代码:
//直接侦听 HTTP 的 System.Web.Http.HttpServer 的实现。
public sealed class HttpSelfHostServer : HttpServer
{
public HttpSelfHostServer(HttpSelfHostConfiguration configuration);
// configuration: 配置。
// dispatcher:调度程序。
public HttpSelfHostServer(HttpSelfHostConfiguration configuration, HttpMessageHandler dispatcher);
// 一个表示异步 System.Web.Http.HttpServer 关闭操作的 System.Threading.Tasks.Task。
public Task CloseAsync();
protected override void Dispose(bool disposing);
// 返回结果:
// 一个表示异步 System.Web.Http.HttpServer 打开操作的 System.Threading.Tasks.Task。一旦成功完成此任务,服务器将运行。
public Task OpenAsync();
}
HttpSelfHostConfiguration
继承自 HttpConfiguration
,有一个 ServicesContainer
类型的属性Services
,此属性有大用‼️
ServicesContainer
体系结构
System.Object
└─System.Web.Http.Controllers.ServicesContainer
└─System.Web.Http.Controllers.ControllerServices
└─System.Web.Http.Services.DefaultServices
ServicesContainer
用于服务管理,有添加修改和删除等操作方法。
IAssembliesResolver
在 ASP.NET Web API 的 HttpController
激活系统中,AssembliesResolver
为目标 HttpController
类型解析提供候选的程序集。该接口定义在命名空间 System.Web.Http.Dispatcher
下。
定义:
public interface IAssembliesResolver
{
ICollection<Assembly> GetAssemblies();
}
在 ASP.NET Web API 有一个重要的类 DefaultAssembliesResolver
继承了 IAssembliesResolver
public class DefaultAssembliesResolver : IAssembliesResolver
{
public virtual ICollection<Assembly> GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>();
}
}
从DefaultAssembliesResolver
的实现上可以看到默认的注射器只能注射当前程序域的程序集。
总结
分析到这里,终于了然了,自自定义的宿主程序中要引入外部的 Controller,必须重新实现 IAssembliesResolver,或者继承 DefaultAssembliesResolver
重写 GetAssemblies()方法。
ASP.NET Web 是一个严谨而具有高扩展性的框架,HttpSelfHostServer
是基于这个框架实现,要实现一个完善的宿主程序,必须对这个机制有一定的了解,利用该『组织』提供的服务和协议,站在巨人肩膀上,才能发现『万有引力』😄!