与 MVC 类似, Web API 提供了System.Web.Http.Services.IDependencyResolver 接口来实现依赖注入, 我们可以很容易的用 Unity 来实现这个接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class UnityDependencyResolver : IDependencyResolver {
 
   private readonly IUnityContainer _container;
 
   public UnityDependencyResolver(IUnityContainer container) {
      this._container = container;
   }
 
   public object GetService(Type serviceType) {
      return this._container.IsRegistered(serviceType) ? this._container.Resolve(serviceType) : null;
   }
 
   public IEnumerable<Object> GetServices(Type serviceType) {
      return this._container.Registrations
             .Where(reg => type.IsAssignableFrom(reg.RegisteredType))
             .Select(reg => string.IsNullOrEmpty(reg.Name) ? this._container.Resolve(type) : this._container.Resolve(type, reg.Name));
   }
 
}

使用 UnityDependencyResolver 的方法也很简单, 只要在 Global.asax.cs 里添加下面一行代码即可:

1
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(new UnityDependencyResolver(container));

将 UnityDependencyResolver 配置好之后, Web API 框架将会在运行时向其请求一系列的接口实现:

  1. 应用启动时, Web API 框架会依次请求下列接口:
    1. System.Web.Http.Dispatcher.IHttpControllerFactory
    2. System.Web.Http.Common.ILogger
    3. System.Web.Http.Dispatcher.IHttpControllerActivator
    4. System.Web.Http.Controllers.IHttpActionSelector
    5. System.Web.Http.Controllers.IHttpActionInvoker
  2. 在第一次访问某个 Controller 之前, 还会请求下面的接口 (如果重复访问相同的 Controller , 则不会再次调用):
    1. System.Web.Http.Filters.IFilterProvider
  3. 每次处理 HTTP 请求时, Web API 请求下列接口:
    1. System.Web.Http.Controllers.IActionValueBinder
    2. System.Web.Http.ValueProviders.ValueProviderFactory (仅 Action 需要参数时才需要)
    3. System.Web.Http.ModelBinding.ModelBinderProvider (仅 Action 需要参数时才需要)
    4. System.Web.Http.Metadata.ModelMetadataProvider (仅 Action 需要参数时才需要)
    5. System.Web.Http.Validation.ModelValidatorProvider (仅 Action 需要参数时才需要)
    6. System.Net.Http.Formatting.IFormatterSelector

这些接口都是 Web API 公开的扩展点, 可以根据需要来对这些接口进行实现, 并通过 Unity 进行配置, 让其注入到 Web API 运行时中。 接下来将逐个讨论这些扩展点。

扩展

IHttpControllerFactory

IHttpControllerFactory 接口有两个方法, 负责创建和销毁 HttpController 实例:

  • CreateController(HttpControllerContext, Type) : IHttpController
  • ReleaseController(IHttpController) : void

这个接口的默认实现是 DefaultHttpControllerFactory , 根据当前请求的上下文通过创建 HttpControllerDescriptor , 然后通过 HttpControllerDescriptor 的 ControllerActivator 创建对应的 IHttpController 实例。

ILogger

只是一个日志接口, 有下面的几个方法:

  • Log(string, TraceLevel, Func) : void
  • LogException(string, TraceLevel, Exception) : void

默认的实现是 DiagnosticLogger , 通过 ILSpy 观察, 貌似什么都没有做。

IHttpControllerActivator

负责创建具体的 Controller 实例, 只有一个方法:

  • Create(HttpControllerContext, Type) : IHttpController

默认的实现是 DefaultHttpControllerActivator , 先向 DependencyResolver 请求对应 Controller 类型的实例, 如果返回为空, 则通过动态编译包装 Controller 类型构造函数的 lambda 表达式进行创建实例, 相关的代码如下:

1
2
3
4
Func<IHttpController> func = TypeActivator.Create<IHttpController>(controllerType);
Tuple<HttpControllerDescriptor, Func<IHttpController>> value = Tuple.Create<HttpControllerDescriptor, Func<IHttpController>>(controllerContext.ControllerDescriptor, func);
Interlocked.CompareExchange<Tuple<HttpControllerDescriptor, Func<IHttpController>>>(ref this._fastCache, value, null);
result = func();

IHttpActionSelector

负责选择合适的动作, 默认的实现是 ApiControllerSelector , 选择规则如下:

  1. 如果路由定义了 {action} , 则通过当前的 HttpControllerContext 中的 action 的值寻找合适的方法;
  2. 否则, 根据当前的 HTTP 请求方法 (POST, GET, PUT, DELETE) 寻找合适的方法。

IHttpActionInvoker

负责调用 HttpActionSelector 选择到的方法, 该接口有一个方法:

  • InvokeActionAsync(HttpActionContext, CancellationToken) : Task<HttpResponseMessage>

默认的实现是 ApiControllerActionInvoker , 通过反射找出动作方法的参数信息, 然后再通过动态创建 lambda 表达式对方法进行调用, 取得返回结果, 部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "instance");
ParameterExpression parameterExpression2 = Expression.Parameter(typeof(object[]), "parameters");
List<Expression> list = new List<Expression>();
ParameterInfo[] parameters = methodInfo.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
   ParameterInfo parameterInfo = parameters[i];
   BinaryExpression expression = Expression.ArrayIndex(parameterExpression2, Expression.Constant(i));
   UnaryExpression item = Expression.Convert(expression, parameterInfo.ParameterType);
   list.Add(item);
}
UnaryExpression instance2 = (!methodInfo.IsStatic) ? Expression.Convert(parameterExpression, methodInfo.ReflectedType) : null;
MethodCallExpression methodCallExpression = Expression.Call(instance2, methodInfo, list);
if (methodCallExpression.Type == typeof(void))
{
   Expression<Action<object, object[]>> expression2 = Expression.Lambda<Action<object, object[]>>(methodCallExpression, new ParameterExpression[]
   {
      parameterExpression,
      parameterExpression2
   });
   Action<object, object[]> voidExecutor = expression2.Compile();
   return delegate(object instance, object[] methodParameters)
   {
      voidExecutor(instance, methodParameters);
      return null;
   }
   ;
}
UnaryExpression body = Expression.Convert(methodCallExpression, typeof(object));
Expression<Func<object, object[], object>> expression3 = Expression.Lambda<Func<object, object[], object>>(body, new ParameterExpression[]
{
   parameterExpression,
   parameterExpression2
});
return expression3.Compile();

取得返回结果之后, 再调用 ApiResponseConverter 的 GetResponseConverter 方法找到合适的 Converter , 最后返回 Task<HttpResponseMessage>

IFilterProvider

负责提供过滤的标记, Web API 框架内置了下面的几个 FilterProvider :

  • EnumerableEvaluatorFilterProvider 负责提供对 IENumerable 的每个元素的转换的标记, 简单的说, 就是负责提供将 Action 方法返回的 IEnumerable 的结果进行自定义转换的标记;
  • QueryCompositionFilterProvider 负责对 Action 方法返回的 IQueryable 的结果进行符合 OData 约定的 URL 参数进行再次过滤的标记 QueryCompositionFilterAttribute , 目前只支持 $filter 、 $orderby 、 $skip 以及 $top ;
  • ActionDescriptorFilterProvider
  • ConfigurationFilterProvider

注意, 这里说的是 FilterProvider, 不是 Filter , 也不是 FilterAttribute 。

IActionValueBinder

负责绑定 Action 方法的参数, 默认的实现是 DefaultActionValuebinder , 通过调用 ValueProviderFactory 、 ModelBinderProvider 进行参数绑定, 支持多种形式的参数绑定, 绑定策略比较复杂, 总的来说是简单的参数从 URL 中绑定, 复杂参数从 HTTP 请求内容中获取。

ValueProviderFactory

定义了 Action 参数从哪里获取, 有以下几个实现, 分别支持从 URI 、 QueryString、 Post 内容中提取参数值:

  • CompositeValueProviderFactory
  • KeyValueModelProviderFactory
  • RouteDataValueProviderFactory
  • QueryStringValueProviderFactory

ModelBinderProvider

定义了如何将获取到的 HTTP 请求的的参数之绑定到指定的参数。 System.Web.Http.ModelBinding.Binders 命名空间内提供了多种 BinderProvider , 应该可以处理大多数常见的类型。

ModelMetadataProvider

负责提供模型元数据描述信息。

ModelValidatorProvider

负责根据元素据信息对模型进行验证。

IFormatterSelector

负责选择合适的格式, 包括客户端请求的格式以及服务端返回的格式, 默认实现是 FormatterSelector , 能够提下面的 MediaFormater :

  • BufferedmediaTypeFormatter 提供对二进制格式的读取与写入;
  • FormUrlEncodedMediaTypeFormatter 提供对表单 URL 编码格式的读取与写入;
  • JsonMediaTypeFormatter 提供对 Json 格式的读取与写入;
  • XmlMediaFormatter 提供对 XML 格式的读取与写入。

Web API 依赖注入与扩展的更多相关文章

  1. 使用Unity 实现ASP.NET Web API 依赖注入

    DI/IoC 的设计前面已经讲过好几次了,简单的一段话说明就是:「目标对象与外部相依的方式仅相依于 interface,而相依 interface 的 instance 透过 constructor ...

  2. 第四节:配置的读取、StartUp类、内置依赖注入和扩展改造

    一. 配置的读取 在Asp.Net Core中,有一个 appsettings.json 文件,用于存储相应的配置信息,读取的时,要通过构造函数注入:IConfiguration Configurat ...

  3. 【17MKH】我在框架中对.Net依赖注入的扩展

    说明 依赖注入(DI)是控制反转(IoC)的一种技术实现,它应该算是.Net中最核心,也是最基本的一个功能.但是官方只是实现了基本的功能和扩展方法,而我呢,在自己的框架 https://github. ...

  4. ODATA WEB API(一)---扩展使用

    一.概述 时间也算充足,抽点时间总结下OData的常用的使用方式,开放数据协议(OData)是一个查询和更新数据的Web协议.OData应用了web技术如HTTP.Atom发布协议(AtomPub)和 ...

  5. Dependency Injection in ASP.NET Web API 2 (在web api2 中使用依赖注入)

    原文:http://www.asp.net/web-api/overview/advanced/dependency-injection 1 什么是依赖注入(Dependency Injection) ...

  6. ASP.NET Web API - 使用 Castle Windsor 依赖注入

    示例代码 项目启动时,创建依赖注入容器 定义一静态容器 IWindsorContainer private static IWindsorContainer _container; 在 Applica ...

  7. web API .net - .net core 对比学习-依赖注入

    今天我们来看一下 .net web api 和 .net core web api依赖注入机制的差异. 首先我们分别在.net web api 和 .net core web api新建文件夹Serv ...

  8. MVC Castle依赖注入实现代码

    1.MVc 实现依赖注入 public class WindsorControllerFactory : DefaultControllerFactory { private readonly IKe ...

  9. ASP.NET Web API中的依赖注入

    什么是依赖注入 依赖,就是一个对象需要的另一个对象,比如说,这是我们通常定义的一个用来处理数据访问的存储,让我们用一个例子来解释,首先,定义一个领域模型如下: namespace Pattern.DI ...

随机推荐

  1. 深度:ARC会导致的内存泄露

    iOS提供了ARC功能,很大程度上简化了内存管理的代码. 但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露. 下面列举两种内存泄露的情况. 1,循环参照 A有个属性参照B,B有个属性 ...

  2. iOS 获取手机的型号,系统版本,软件名称,软件版本

    转载自:http://www.2cto.com/kf/201210/162333.html   网上搜索出来的,记录下来以后使用方便: [java]//手机序列号      NSString* ide ...

  3. Integer自动装箱分析

    先看看下面的代码会输出什么: public static void main(String[] args) { Integer i = 127; Integer j = 128; Integer ii ...

  4. Mapped Statements collection does not contain value for TaskMapper.selectByPrimaryKey

    Mapped Statements collection does not contain value for后面是什么类什么方法之类的: 错误原因有几种: 1.mapper.xml中没有加入name ...

  5. input输入框的各种样式

    输入框景背景透明: <input style="background:transparent;border:1px solid #ffffff"> 鼠标划过输入框,输入 ...

  6. WinForm 控件库

    1:Telerik 介绍: Telerik 是保加利亚的一个软件公司,专注于微软.Net平台的表示层与内容管理控件.Telerik 提供高度稳定性和丰富性能的组件产品,并可应用在非常严格的环境中. 现 ...

  7. bnu 4353 删格游戏(博弈)

    http://www.bnuoj.com/bnuoj/problem_show.php?pid=4353 [题意]:每次选择一个小格后,该小格的右上部分就全被删去了,且要求每次至少删去一个格子,选中左 ...

  8. 1040: [ZJOI2008]骑士 - BZOJ

    Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火 ...

  9. 常用汇编命令&&OD命令总结

    汇编32位CPU所含有的寄存器有: 4个数据寄存器(EAX.EBX.ECX和EDX)对低16位数据的存取,不会影响高16位的数据.这些低16位寄存器分别命名为:AX.BX.CX和DX,它和先前的CPU ...

  10. python datetime笔记

    python datetime笔记 http://mint-green.diandian.com/post/2011-09-09/4892024 获取当前时间,并通过字符串输出. 格式为:%Y-%m- ...