惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 俩醒叁醉

SQL2000安装问题(转) - 俩醒叁醉 sql server 2008安装需要一直重启。但重启后又没有达到效果。 - 俩醒叁醉 为数据库中所有的用户数据表生成分页存储过程 SQL 2005 字段备注获取 __doPostBack方法解析 - 俩醒叁醉 如何在三个月掌握三年的经验(转载&&笔记) - 俩醒叁醉 jQuery Ajax的使用 JQuery资源网站 - 俩醒叁醉 Cookie跨域、虚拟目录 深入分析跨域cookie的问题 CnBlogsDotText使用实例 - 俩醒叁醉 轻松搭建博客平台-开源ASP.NET 博客Subtext 的安装 表达式目录树(源MSDN) Web 2.0 编程思想:16条法则 Control Adapter 以下代码提供查询数据库中是否存在某个值 URL Routing MVC Controllers和Forms验证 CSharp——Lambda 表达式
ASP.NET MVC 4 WebAPI. Support Areas in HttpControllerSelector
俩醒叁醉 · 2014-02-27 · via 博客园 - 俩醒叁醉

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()

{

            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 }

    );

}


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 }

);