Web API 依赖注入与扩展
与 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 框架将会在运行时向其请求一系列的接口实现:
- 应用启动时, Web API 框架会依次请求下列接口:
- System.Web.Http.Dispatcher.IHttpControllerFactory
- System.Web.Http.Common.ILogger
- System.Web.Http.Dispatcher.IHttpControllerActivator
- System.Web.Http.Controllers.IHttpActionSelector
- System.Web.Http.Controllers.IHttpActionInvoker
- 在第一次访问某个 Controller 之前, 还会请求下面的接口 (如果重复访问相同的 Controller , 则不会再次调用):
- System.Web.Http.Filters.IFilterProvider
- 每次处理 HTTP 请求时, Web API 请求下列接口:
- System.Web.Http.Controllers.IActionValueBinder
- System.Web.Http.ValueProviders.ValueProviderFactory (仅 Action 需要参数时才需要)
- System.Web.Http.ModelBinding.ModelBinderProvider (仅 Action 需要参数时才需要)
- System.Web.Http.Metadata.ModelMetadataProvider (仅 Action 需要参数时才需要)
- System.Web.Http.Validation.ModelValidatorProvider (仅 Action 需要参数时才需要)
- 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 , 选择规则如下:
- 如果路由定义了 {action} , 则通过当前的 HttpControllerContext 中的 action 的值寻找合适的方法;
- 否则, 根据当前的 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 依赖注入与扩展的更多相关文章
- 使用Unity 实现ASP.NET Web API 依赖注入
DI/IoC 的设计前面已经讲过好几次了,简单的一段话说明就是:「目标对象与外部相依的方式仅相依于 interface,而相依 interface 的 instance 透过 constructor ...
- 第四节:配置的读取、StartUp类、内置依赖注入和扩展改造
一. 配置的读取 在Asp.Net Core中,有一个 appsettings.json 文件,用于存储相应的配置信息,读取的时,要通过构造函数注入:IConfiguration Configurat ...
- 【17MKH】我在框架中对.Net依赖注入的扩展
说明 依赖注入(DI)是控制反转(IoC)的一种技术实现,它应该算是.Net中最核心,也是最基本的一个功能.但是官方只是实现了基本的功能和扩展方法,而我呢,在自己的框架 https://github. ...
- ODATA WEB API(一)---扩展使用
一.概述 时间也算充足,抽点时间总结下OData的常用的使用方式,开放数据协议(OData)是一个查询和更新数据的Web协议.OData应用了web技术如HTTP.Atom发布协议(AtomPub)和 ...
- Dependency Injection in ASP.NET Web API 2 (在web api2 中使用依赖注入)
原文:http://www.asp.net/web-api/overview/advanced/dependency-injection 1 什么是依赖注入(Dependency Injection) ...
- ASP.NET Web API - 使用 Castle Windsor 依赖注入
示例代码 项目启动时,创建依赖注入容器 定义一静态容器 IWindsorContainer private static IWindsorContainer _container; 在 Applica ...
- web API .net - .net core 对比学习-依赖注入
今天我们来看一下 .net web api 和 .net core web api依赖注入机制的差异. 首先我们分别在.net web api 和 .net core web api新建文件夹Serv ...
- MVC Castle依赖注入实现代码
1.MVc 实现依赖注入 public class WindsorControllerFactory : DefaultControllerFactory { private readonly IKe ...
- ASP.NET Web API中的依赖注入
什么是依赖注入 依赖,就是一个对象需要的另一个对象,比如说,这是我们通常定义的一个用来处理数据访问的存储,让我们用一个例子来解释,首先,定义一个领域模型如下: namespace Pattern.DI ...
随机推荐
- Xubuntu 安装mentohust
对于路由器上网到用户来说,自动分配IP上网。 对于校园网用户,首先下载mentohust_0.3.4-1_i386.deb,双击安装程序 然后在命令窗口中输入sudo -s 密码:user来获得roo ...
- [译] ASP.NET 生命周期 – ASP.NET 上下文对象(七)
使用 HttpRequest 对象 HttpRequest 对象描述的是一个正在被处理的 HTTP 请求.下表列举了 HttpRequest 中的属性,它们提供了当前请求的相关信息(HttpReque ...
- XSS 复合编码 续
对上文 [web安全]第二弹:XSS攻防中的复合编码问题 的一些补充,思路来源于:http://escape.alf.nu/3/ html解码的问题: 通过appendChild添加的节点,不会被HT ...
- validate[.unobtrusive]和Bootstrap实现tooltip错误提示
validate[.unobtrusive]和Bootstrap实现tooltip错误提示 类似的文章园子里已有,请看这里,个人感觉稍显复杂,日前也打算写一个简单的给项目用,一些关键点记录于此.最终效 ...
- 搭建Git Server
windows上如何搭建Git Server Git在版本控制方面,相比与SVN有更多的灵活性,对于开源的项目,我们可以托管到Github上面,非常方便,但是闭源的项目就会收取昂贵的费用.那么私有 ...
- php文件上传大小限制的修改方法大全
php文件上传大小限制的修改方法大全 基本就是修改maxsize选项,当然为了提高上传文件的成功率,还需要设置超时时间等. 文章如下: [php文件上传]php文件上传大小限制修改,phpmyadmi ...
- shell复习---文件解压命令
需要自己部署服务端,故在申请了空间之后,需要自己安装linux自己,自己安装Apache等,所以下载的压缩文件需要运行.网上找了一些解压命令不对,特别试了下面的方法有效,特别记录一下: 用ssh 登陆 ...
- sql视图学习笔记--视图
视图是为用户对数据多种显示需求而创建的,其主要用在一下几种情况: (1)限制用户只能访问特定表特定条件的内容,提高系统的安全性. (2)隐藏表结构.创建多种形式的数透视,满足不同用户需求. (3)将复 ...
- 2001: [Hnoi2010]City 城市建设 - BZOJ
DescriptionPS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁.Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费.Louis希望建造最少的 ...
- 1038: [ZJOI2008]瞭望塔 - BZOJ
Description 致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安.我们将H村抽象为一维的轮廓.如下图所示 我们可以用一条山的上方轮廓折线(x1, ...