与 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. Mysql 配置主从服务自动同步功能

    1.修改主服务器master:   #vi /etc/my.cnf       [mysqld]       log-bin=mysql-bin   //[必须]启用二进制日志       serve ...

  2. go语言使用redis —— redigo

    redis的client有好多好多,go语言的client在redis官方有两个推荐,radix和redigo.选择哪一个好呢?确实很纠结,后来掷硬币决定选择redigo了. redis.go.red ...

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

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

  4. 【BZOJ 2654】tree

    Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色 ...

  5. JVM -XX: 参数介绍(转)

    垃圾回收器相关常用参数: 功能开关: 参数 默认值或限制 说明 参数 默认值 功能 -XX:-AllowUserSignalHandlers 限于Linux和Solaris,默认不启用 允许为java ...

  6. DB天气app冲刺二阶段第一天

    原来找人也是个力气活...好费劲呀..今天的进度有点慢,,确切的说是没有什么进度 因为不会弄了..加上今天一个劲的找同学帮忙写评论.心思没定下来 根本没思路了..明天按照今天的计划继续冲刺..

  7. Android Studio 单刷《第一行代码》系列 04 —— Activity 相关

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

  8. hdu 1561

    有依赖的背包,用树形dp解 #include<iostream> #include<cstdio> #include<cstring> #include<al ...

  9. Fiddler 日志

    Fiddler 日志(Logging) 在开发扩展插件及编写FiddlerScript时对调试程序非常有用. 1.输出日志 在FiddlerScript脚本中,你可以这样输出输出日志: Fiddler ...

  10. fork与vfork的区别

    fork()与vfock()都是创建一个进程,那他们有什么区别呢?总结有以下三点区别: 1.  fork  ():子进程拷贝父进程的数据段,代码段     vfork ( ):子进程与父进程共享数据段 ...