ASP.NET MVC 4 WebAPI. Support Areas in HttpControllerSelector
This article was written for ASP.NET MVC 4 RC (Release Candidate). If you are still using Beta version of ASP.NET MVC 4 then you have to read the previous article.
HttpControllerFactory was deleted in ASP.NET MVC 4 RC. Actually, it was replaced by two interfaces: IHtttpControllerActivator and IHttpControllerSelector.
Unfortunately DefaultHttpControllerSelector still doesn't support Areas by default. To support it you have to write your HttpControllerSelector from scratch. To be honest, I will derive my selector from DefaultHttpControllerSelector.
In this post I will show you how you can do it.
AreaHttpControllerSelector
First of all, you have to derive your class from DefaultHttpControllerSelector class:
- public class AreaHttpControllerSelector : DefaultHttpControllerSelector
- {
- private readonly HttpConfiguration _configuration;
- public AreaHttpControllerSelector(HttpConfiguration configuration)
- : base(configuration)
- {
- _configuration = configuration;
- }
- }
In the constructor mentioned above I called the base constructor and stored the HttpConfiguration. We will use it a little bit later.
My code will use two constants:
- private const string ControllerSuffix = "Controller";
- private const string AreaRouteVariableName = "area";
You can understand why we need first one by name. The second one contains the name of the variable which we will use to specify area name in Routes collection.
Somewhere we have to store all of the API controllers.
- private Dictionary<string, Type> _apiControllerTypes;
- private Dictionary<string, Type> ApiControllerTypes
- {
- get { return _apiControllerTypes ?? (_apiControllerTypes = GetControllerTypes()); }
- }
- private static Dictionary<string, Type> GetControllerTypes()
- {
- var assemblies = AppDomain.CurrentDomain.GetAssemblies();
- var types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix) && typeof(IHttpController).IsAssignableFrom(t)))
- .ToDictionary(t => t.FullName, t => t);
- return types;
- }
Method GetControllerTypes takes all the API controllers types from all of your assemblies, and store it inside the dictionary, where the key is FullName of the type and value is the type itself.
Of course we will set this dictionary only once. And then just use it.
Now we are ready to implement one of the important method:
- public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
- {
- return GetApiController(request) ?? base.SelectController(request);
- }
In that method I try to take the HttpControllerDescriptor from method GetApiController and if it return null then call the base method.
And additional methods:
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
36
37
38
39
|
private static string GetAreaName(HttpRequestMessage request) { var data = request.GetRouteData(); if (!data.Values.ContainsKey(AreaRouteVariableName)) { return null ; } return data.Values[AreaRouteVariableName].ToString().ToLower(); } private Type GetControllerTypeByArea( string areaName, string controllerName) { var areaNameToFind = string .Format( ".{0}." , areaName.ToLower()); var controllerNameToFind = string .Format( ".{0}{1}" , controllerName, ControllerSuffix); return ApiControllerTypes.Where(t => t.Key.ToLower().Contains(areaNameToFind) && t.Key.EndsWith(controllerNameToFind, StringComparison.OrdinalIgnoreCase)) .Select(t => t.Value).FirstOrDefault(); } private HttpControllerDescriptor GetApiController(HttpRequestMessage request) { var controllerName = base .GetControllerName(request); var areaName = GetAreaName(request); if ( string .IsNullOrEmpty(areaName)) { return null ; } var type = GetControllerTypeByArea(areaName, controllerName); if (type == null ) { return null ; } return new HttpControllerDescriptor(_configuration, controllerName, type); } |
Method GetAreaName just takes area name from HttpRequestMessage.
Method GetControllerTypeByArea are tries to find the controller in the ApiControllerTypes by full name of the controller where the full name contains area's name surrounded by "." (e.g. ".Admin.") and ends with controller name + controller suffix (e.g. UsersController).
And if a controller type found then method GetApiController will create and return back HttpControllerDescriptor.
So, my AreaHttpControllerSelector is ready to be registered in my application.
Registering AreaHttpControllerSelector
The next thing you have to do is to say to your application to use this controller selector instead of DefaultHttpControllerSelector. And fortunately it is really easy - just add one additional line to the end of Application_Start method in Glogal.asax file:
1
2
3
4
5
|
protected void Application_Start() { // your default code GlobalConfiguration.Configuration.Services.Replace( typeof (IHttpControllerSelector), new AreaHttpControllerSelector(GlobalConfiguration.Configuration)); } |
That's all.
Using AreaHttpControllerSelector
If you did everything right, now you can forget about that "nightmare" code mentioned above. And just start to use it!
You have to add new HttpRoute to your AreaRegistration.cs file:
1
2
3
4
5
6
7
8
9
10
|
public override void RegisterArea(AreaRegistrationContext context) { context.Routes.MapHttpRoute( name: "Admin_Api" , routeTemplate: "api/admin/{controller}/{id}" , defaults: new { area = "admin" , id = RouteParameter.Optional } ); // other mappings } |
Or just use one global route in your Global.asax like:
1
2
3
4
5
|
routes.MapHttpRoute( name: "DefaultApi" , routeTemplate: "api/{area}/{controller}/{id}" , defaults: new { id = RouteParameter.Optional } );
|
ASP.NET MVC 4 WebAPI. Support Areas in HttpControllerSelector的更多相关文章
- 给Asp.Net MVC及WebApi添加路由优先级
一.为什么需要路由优先级 大家都知道我们在Asp.Net MVC项目或WebApi项目中注册路由是没有优先级的,当项目比较大.或有多个区域.或多个Web项目.或采用插件式框架开发时,我们的路由注册很可 ...
- 【转载】为ASP.NET MVC及WebApi添加路由优先级
路由方面的: 转载地址:http://www.jb51.net/article/73417.htm Author:lijiao 这是一个对Asp.Net Mvc的一个很小的功能拓展,小项目可能不太需要 ...
- (转)ASP.NET Mvc 2.0 - 1. Areas的创建与执行
转自:http://www.cnblogs.com/terrysun/archive/2010/04/13/1711218.html ASP.NET Mvc 2.0 - 1. Areas的创建与执行 ...
- AJAX跨域调用ASP.NET MVC或者WebAPI服务
关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案 作者:陈希章 时间:2014-7-3 问题描述 当跨域(cross domain)调用ASP.NET MVC或者ASP. ...
- AJAX跨域调用ASP.NET MVC或者WebAPI服务的解决方案
问题描述 当跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况. 重现方式 使用模板创建一个最简单的ASP.NET Web ...
- ASP.NET MVC对WebAPI接口操作(添加,更新和删除)
昨天<怎样操作WebAPI接口(显示数据)>http://www.cnblogs.com/insus/p/5670401.html 既有使用jQuery,也有使作HttpClient来从数 ...
- 关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案
作者:陈希章 时间:2014-7-3 问题描述 当跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况. 重现方式 使用模 ...
- ASP.NET MVC 4 WebAPI Simple Sample
// Controllers.cs namespace Microshaoft.WebApi.Controllers { using Microshaoft.WebApi.Models; using ...
- Asp.Net MVC part6 WebAPI
两种web服务SOAP风格:基于方法,产品是WebServiceREST风格:基于资源,产品是WebAPI可以返回json.xml类型的数据对于数据的增.删.改.查,提供相对的资源操作,按照请求的类型 ...
随机推荐
- 图解 javascript 作用域链
还是之前那一段简单的javascript代码: window.onload=function(){ function sub(a,b){ return a-b; } var result=sub(10 ...
- Delphi中有序型
有序类型包括:.integer(整型).character(字符型).boolean(布尔型).enumerated(枚举型).subrange(子界型)有序类型定义了一组被排序的值.每个相异值都有唯 ...
- 关于c语言中的字符数组和字符串指针
先看代码: #include <stdio.h> int main(void) { ] = "; char * strTmp = "abcdefg"; int ...
- 机器学习(一):梯度下降、神经网络、BP神经网络
这几天围绕论文A Neural Probability Language Model 看了一些周边资料,如神经网络.梯度下降算法,然后顺便又延伸温习了一下线性代数.概率论以及求导.总的来说,学到不少知 ...
- Jquery 中map和each的区别
<script type="text/javascript"> $(function () { var json = {"Name":"L ...
- Sql Server总结
主键 主键就是数据行的唯一标识.不会重复的列,才能当主键.一个表可以没有主键,但是会非常难以处理,因此没有特殊理由表都要设定主键.主键有两种选用策略:业务主键和逻辑主键. 业务主键是使用有业务意义的 ...
- Server Library [Apache Tomcat v6.0](unbound)服务未绑定解决办法
(1) 单击File按钮---钩选Show AllWizard——>选择Server——>单击Next (2)Add Library 选择 WTP Server Runtime(My ...
- The first day of HTML
这是韩顺平老师的<轻松搞定网页设计(html.css.js)>,讲的还凑合,仅作入门.决定还是做好笔记,记录学习的过程,这是HTML的第一天. HTML(HyperText Mark-up ...
- 使用jmeter对websocket进行压力测试[转载]
前段时间本着练习angularJS+requireJS的目的写了一个基于nodeJS和socket.io的聊天室,github地址为:https://github.com/towersxu/node- ...
- copy
拷贝文件,不覆盖重复文件 yes no|cp -i a b