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类型的数据对于数据的增.删.改.查,提供相对的资源操作,按照请求的类型 ...
 
随机推荐
- 彻底理解js中this的指向
			
首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问题,虽然 ...
 - slot的含义
			
1) slot就是槽的意思,是一个资源单位,只有给task分配了一个slot之后,这个task才可以运行.slot分两种,map slot沪蓉reduce slot.另外,slot是一个逻辑概念,一个 ...
 - nginx js、css多个请求合并为一个请求(concat模块)
			
模块介绍 mod_concat模块由淘宝开发,目前已经包含在tengine中,并且淘宝已经在使用这个nginx模块.不过塔暂时没有包含在nginx中.这个模块类似于apache中的modconcat. ...
 - sshd_config配置 详解
			
原文:http://blog.licess.org/sshd_config/ # 1. 关于 SSH Server 的整体设定,包含使用的 port 啦,以及使用的密码演算方式 Port 22 # S ...
 - iOS富文本-NSAttributedString简单封装
			
直接调用系统的写起来比较麻烦,封装一下 因为要简单所以就写类方法 WJAttributeStyle 基类 ) { ; i < styles.count; i ++) { ...
 - mysql 存储过程事务
			
DECLARE t_error INTEGER DEFAULT ; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error=; START TRAN ...
 - hdu 3118(二进制枚举)
			
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3118 思路:题目要求是去掉最少的边使得图中不存在路径长度为奇数的环,这个问题等价于在图中去掉若干条边, ...
 - 妙味课堂——HTML+CSS(第四课)(二)
			
单开一篇来讲一个大点的话题——清浮动 来看下例: <!DOCTYPE html> <html> <head> <meta charset="U ...
 - lintcode:恢复IP地址
			
恢复IP地址 给一个由数字组成的字符串.求出其可能恢复为的所有IP地址. 样例 给出字符串 "25525511135",所有可能的IP地址为: [ "255.255.11 ...
 - lintcode:线段树的修改
			
线段树的修改 对于一棵 最大线段树, 每个节点包含一个额外的 max 属性,用于存储该节点所代表区间的最大值. 设计一个 modify 的方法,接受三个参数 root. index 和 value.该 ...