ASP.NET MVC应用框架是通过扩展ASP.NET实现的。ASP.NET的扩展点体现在HttpModule和HttpHandler两个核心组件上,ASP.NET MVC框架就是通过自定义HttpModule(UrlRoutingModule)HttpHandler(MvcHandler)实现的。

自定义HttpModule

UrlRouteModule==>RouteTable==>Route==>RouteData

自定义Module:UrlRouteModule

解析HTTP请求获取一个以Controller和Action名称为核心的路由数据RouteData。

注册自定义Module

1
2
3
4
5
6
7
<!--在配置文件Web.Config中添加如下配置项:-->
<!--自定义Module-->
<system.webServer>
<modules>
<add name="UrlRoutingModule" type="CHCMVC.Module.UrlRoutingModule"/>
</modules>
</system.webServer>

注册Route对象

1
2
3
4
5
6
7
//添加ASP.NET应用程序文件Global.asax
//在Application_Start方法中注册Route
protected void Application_Start(object sender, EventArgs e)
{
//添加路由对象到路由表中
RouteTable.Routes.Add("default", new Route.Route { Url = "{controller}/{action}" });
}

路由表RouteTable

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
40
41
42
43
44
45
//路由表:路由对象的集合,静态属性Routes,通过Global.asax文件已注册路由对象
namespace CHCMVC.Route
{
/// <summary>
/// 同一个Web应用可以采用不同的URL模式,所以需要注册多个路由对象,就组成了一个路由表
/// </summary>
public class RouteTable
{
/// <summary>
/// 路由对象列表
/// </summary>
public static RouteDictionary Routes { get; set; }
static RouteTable()
{
Routes = new RouteDictionary();
}
}
}

//路由表中的静态属性Routes,通过遍历路由列表,
//调用每个路由对象Route的GetRouteData方法可以获取与当前请求匹配的路由数据RouteData。
namespace CHCMVC.Route
{
public class RouteDictionary : Dictionary<string, RouteBase>
{
/// <summary>
/// 获取与请求匹配的路由数据对象
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public RouteData GetRouteData(HttpContextBase httpContext)
{
foreach (var route in this.Values)
{
RouteData routeData = route.GetRouteData(httpContext);

if (null != routeData)
{
return routeData;
}
}
return null;
}
}
}

路由对象Route

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//通过路由对象Route,可以获取该路由对象的路由数据RouteData。
//RouteData继承抽象类RouteBase,实现抽象方法GetRouteData。
namespace CHCMVC.Route
{
public abstract class RouteBase
{
public abstract RouteData GetRouteData(HttpContextBase httpContext);
}
}

//将请求的URL与路由对象的URL(已在Global.asax文件中注册)匹配
//路由对象里面的RouteHandler来源于自定义的Handler,MvcRouteHandler==>MvcHandler会赋给RouteData。
namespace CHCMVC.Route
{
/// <summary>
/// 路由数据RouteData里面的属性,来源于路由对象Route的同名属性
/// </summary>
public class Route : RouteBase
{
public IRouteHandler RouteHandler { get; set; }
/// <summary>
/// 当前请求的URL
/// </summary>
public string Url { get; set; }
public IDictionary<string, object> DataTokens { get; set; }

public Route()
{
this.DataTokens = new Dictionary<string, object>();
this.RouteHandler = new MvcRouteHandler();//自定义Handler
}

/// <summary>
/// 通过HttpContextBase获取请求地址,创建RouteData对象
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public override RouteData GetRouteData(HttpContextBase httpContext)
{
IDictionary<string, object> variables;
if (this.Match(httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2), out variables))
{
RouteData routeData = new RouteData();
foreach (var item in variables)
{
routeData.Values.Add(item.Key, item.Value);
}

foreach (var item in DataTokens)
{
routeData.DataTokens.Add(item.Key, item.Value);
}

routeData.RouteHandler = this.RouteHandler;

return routeData;
}

return null;
}

/// <summary>
/// 将请求的URL与路由对象Route中的URL比较,获取请求URL的Controller和Action
/// </summary>
/// <param name="requestUrl"></param>
/// <param name="variables"></param>
/// <returns></returns>
protected bool Match(string requestUrl, out IDictionary<string, object> variables)
{
variables = new Dictionary<string, object>();
string[] strArray1 = requestUrl.Split('/');
string[] strArray2 = this.Url.Split('/');

if (strArray1.Length != strArray2.Length)
{
return false;
}

for (int i = 0; i < strArray2.Length; i++)
{
if (strArray2[i].StartsWith("{") && strArray2[i].EndsWith("}"))
{
variables.Add(strArray2[i].Trim("{}".ToArray()), strArray1[i]);
}
}
return true;
}
}
}

路由数据RouteData

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//通过自定义HttpModule【UrlRouteModule】获得路由数据RouteData。
//路由数据保存了对应的Controller和Action名称,通过反射实现激活。
namespace CHCMVC.Route
{
/// <summary>
/// ASP.NET MVC中的路由数据:RouteData,以Controller和Action名称为核心
/// </summary>
public class RouteData
{
/// <summary>
/// Values:从请求地址中解析出的变量==》来源于Route:Values
/// </summary>
public IDictionary<string, object> Values { get; set; }
/// <summary>
/// DataTokes:其他变量==》来源于Route:DataTokens
/// </summary>
public IDictionary<string, object> DataTokens { get; set; }
/// <summary>
/// 处理Http请求==》来源于Route:RouteHandler
/// </summary>
public IRouteHandler RouteHandler { get; set; }


public RouteData()
{
this.Values = new Dictionary<string, object>();
this.DataTokens = new Dictionary<string, object>();
this.DataTokens.Add("namespaces", new List<string>());
}

//从Values中获取Controller名称
public string Controller
{
get
{
object controllerName = string.Empty;
this.Values.TryGetValue("controller", out controllerName);
return controllerName.ToString();
}
}
//从Values获取Action名称
public string Action
{
get
{
object actionName = string.Empty;
this.Values.TryGetValue("action", out actionName);
return actionName.ToString();
}
}
//从DataTokens中获取Namespaces名称
public IEnumerable<string> Namespaces
{
get
{
return (IEnumerable<string>)this.DataTokens["namespaces"];
}
}
}
}

UrlRouteModule实现

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
40
41
42
43
44
45
46
47
48
49
//自定义的HttpModule,实现IHttpModule接口,在初始化方法Init中注册PostResolveRequestCache事件。
//在事件中调用Route对象的GetRouteData方法获取路由数据RouteData。
//将路由数据RouteData和Http请求上下文HttpContext封装一起,供Controller激活使用。
//通过RouteHandler调用GetHttpHandler获取自定义的HandlerHandler
//调用RemapHandler触发HttpHandler处理请求,激活Controller
namespace CHCMVC.Module
{
/// <summary>
/// 自定义HttpModle:处理Http请求,解析出路由数据
/// </summary>
public class UrlRoutingModule : IHttpModule
{
public void Dispose()
{

}

public void Init(HttpApplication context)
{
context.PostResolveRequestCache += context_PostResolveRequestCache;
}

protected virtual void context_PostResolveRequestCache(object sender, EventArgs e)
{
HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
//从路由表中获取路由数据
RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
if (null == routeData) return;

RequestContext requestContext = new RequestContext { RouteData = routeData, HttpContext = httpContext };

IHttpHandler handler = routeData.RouteHandler.GetHttpHandler(requestContext);
httpContext.RemapHandler(handler);
}
}
}

//由于Controller激活需要用到路由数据RouteData,因此需要封装一起
namespace CHCMVC.Context
{
/// <summary>
/// 当前请求上下文和路由数据RouteData的封装
/// </summary>
public class RequestContext
{
public virtual HttpContextBase HttpContext { get; set; }
public virtual RouteData RouteData { get; set; }
}
}

自定义HttpHandler

MvcRouteHandler==>MvcHandler==>ControllerFactory==>Controller

ControllerActionInvoker==>ModelBinder==>ActionResult

自定义Handler:MvcRouteHandler

根据Controller名称通过反射激活Controller,根据Action名称通过反射执行Action。

注册Controller默认工厂

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
40
41
//添加ASP.NET应用程序文件Global.asax
//在Application_Start方法中注册Route
protected void Application_Start(object sender, EventArgs e)
{
//添加路由对象到路由表中
RouteTable.Routes.Add("default", new Route.Route { Url = "{controller}/{action}" });
//设置激活Controller的工厂
ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory());
//设置激活Controller的命名空间
ControllerBuilder.Current.DefaultNamespaces.Add("CHCMVC.Controller");
}

//默认工厂的注册通过ControllerBuilder实现
namespace CHCMVC.Controller
{
public class ControllerBuilder
{
private Func<IControllerFactory> factoryThunk;
public HashSet<string> DefaultNamespaces { get; set; }
public static ControllerBuilder Current { get; private set; }
//单例
static ControllerBuilder()
{
Current = new ControllerBuilder();
}
public ControllerBuilder()
{
this.DefaultNamespaces = new HashSet<string>();
}
//获取默认工厂
public IControllerFactory GetControllerFactory()
{
return factoryThunk();
}
//设置默认工厂
public void SetControllerFactory(IControllerFactory controllerFactory)
{
factoryThunk = () => controllerFactory;
}
}
}

Controller工厂实现

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//工厂接口
namespace CHCMVC.Controller
{
/// <summary>
/// Controller对象的激活是通过工厂模式实现的
/// </summary>
public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
}
}

//工厂接口实现
//工厂的默认实现已经在Global.asax文件中注册,保存了所有实现IController接口的Controller类型
//工厂的CreateController用于Controller的创建
namespace CHCMVC.Controller
{
/// <summary>
/// 激活Controller的默认工厂,通过反射创建Controller
/// </summary>
public class DefaultControllerFactory : IControllerFactory
{
private List<Type> controllerTypes = new List<Type>();

public DefaultControllerFactory()
{
//通过BuildManager加载所有的程序集,并保存了实现了IController接口的类型
foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
{
foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)))
{
controllerTypes.Add(type);
}
}
}

/// <summary>
/// 根据Controller名字,通过反射创建Controller
/// </summary>
/// <param name="requestContext"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
public IController CreateController(RequestContext requestContext, string controllerName)
{
string typeName = controllerName + "Controller";

List<string> namespaces = new List<string>();

namespaces.AddRange(requestContext.RouteData.Namespaces);
namespaces.AddRange(ControllerBuilder.Current.DefaultNamespaces);

foreach (var ns in namespaces)
{
string controllerTypeName = string.Format("{0}.{1}", ns, typeName);

Type controllerType = controllerTypes.FirstOrDefault(type => string.Compare(type.FullName, controllerTypeName, true) == 0);

if (null != controllerType)
{
return (IController)Activator.CreateInstance(controllerType);
}
}
return null;
}
}
}

MvcRouteHandler激活Controller

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
40
41
42
43
44
45
//MvcRouteHandler已经在路由对象Route中实例化
//在自定义HttpModule中触发ProcessRequest方法,通过调用ControllerFactory创建Controller
namespace CHCMVC.Handler
{
public class MvcRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MvcHandler(requestContext);
}
}
}

//Controller创建(激活)在自定义HttpModule(UrlRouteModule)中触发
//调用Controller工厂实现
//MvcHandler调用ActionInvoker的Execute方法实现Action执行(激活)
namespace CHCMVC.Handler
{
/// <summary>
/// 自定义Handler:实现对Controller激活和Action执行
/// </summary>
public class MvcHandler : IHttpHandler
{
public RequestContext RequestContext { get; set; }

public MvcHandler(RequestContext requestContext)
{
this.RequestContext = requestContext;
}

public bool IsReusable
{
get { return false; }
}

public void ProcessRequest(HttpContext context)
{
string controllerName = this.RequestContext.RouteData.Controller;
IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();

IController controller = controllerFactory.CreateController(this.RequestContext, controllerName);
controller.Execute(this.RequestContext);
}
}
}

Action执行

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//ControllerBase是请求Controller的基类
//实例化ControllerActionInvoker
//实现对Action的执行
//由于Action的激活,离不开Controller,需要通过Controller反射方法,因此需要将Controller和RequestContext封装,供Action激活使用
namespace CHCMVC.Controller
{
public class ControllerBase : IController
{
protected IActionInvoker ActionInvoker { get; set; }
public ControllerBase()
{
this.ActionInvoker = new ControllerActionInvoker();
}
public void Execute(RequestContext requestContext)
{
ControllerContext context = new ControllerContext { RequestContext = requestContext, Controller = this };

string actionName = requestContext.RouteData.Action;

this.ActionInvoker.InvokeAction(context, actionName);
}
}
}

//Action的执行,可能Action拥有参数,因此需要参数绑定
//Action执行之后,返回ActionResult,在View中显示
namespace CHCMVC.Action
{
public class ControllerActionInvoker : IActionInvoker
{
/// <summary>
/// 参数绑定
/// </summary>
public IModelBinder ModelBinder { get; private set; }
public ControllerActionInvoker()
{
this.ModelBinder = new DefaultModelBinder();
}
public void InvokeAction(ControllerContext controllerContext, string actionName)
{
MethodInfo method = controllerContext.Controller.GetType().GetMethods().First(m => string.Compare(actionName, m.Name, true) == 0);

List<object> parameters = new List<object>();

foreach (ParameterInfo parameter in method.GetParameters())
{
parameters.Add(this.ModelBinder.BindModel(controllerContext, parameter.Name, parameter.ParameterType));
}

ActionResult actionResult = method.Invoke(controllerContext.Controller, parameters.ToArray()) as ActionResult;

actionResult.ExecuteResult(controllerContext);
}
}
}

Model绑定

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//Model绑定接口
namespace CHCMVC.Model
{
public interface IModelBinder
{
object BindModel(ControllerContext controllerContext,string modelName,Type modelType);
}
}

//Model绑定接口实现
namespace CHCMVC.Model
{
/// <summary>
/// 参数绑定:
/// 来源:HTTP-POST Form、 RouteData、DataTokens
/// 简单类型:匹配
/// 复杂类型:反射
/// </summary>
public class DefaultModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, string modelName, Type modelType)
{
if (modelType.IsValueType || typeof(string) == modelType)
{
object instance;

if (GetValueTypeInstance(controllerContext, modelName, modelType, out instance))
{
return instance;
}

return Activator.CreateInstance(modelType);
}

object modelInstance = Activator.CreateInstance(modelType);

foreach (PropertyInfo property in modelType.GetProperties())
{
if (!property.CanWrite || (!property.PropertyType.IsValueType && property.PropertyType != typeof(string)))
{
continue;
}

object propertyValue;

if (GetValueTypeInstance(controllerContext, property.Name, property.PropertyType, out propertyValue))
{
property.SetValue(modelInstance, propertyValue, null);
}

}
return modelInstance;

}


private bool GetValueTypeInstance(ControllerContext controllerContex, string modelName, Type modelType, out object value)
{
var form = HttpContext.Current.Request.Form;

string key;
//HTTPPOST Form
if (null != form)
{
key = form.AllKeys.FirstOrDefault(k => string.Compare(k, modelName, true) == 0);
if (key != null)
{
value = Convert.ChangeType(form[key], modelType);
return true;
}
}

//RouteData.Values
key = controllerContex.RequestContext.RouteData.Values.Where(
item => string.Compare(item.Key, modelName, true) == 0).Select(item => item.Key).FirstOrDefault();

if (null != key)
{
value = Convert.ChangeType(controllerContex.RequestContext.RouteData.Values[key], modelType);
return true;
}

//RouteData.DataTokens
key = controllerContex.RequestContext.RouteData.DataTokens
.Where(item => string.Compare(item.Key, modelName, true) == 0)
.Select(item => item.Key).FirstOrDefault();
if (null != key)
{
value = Convert.ChangeType(controllerContex.RequestContext.RouteData.DataTokens[key], modelType);
return true;
}
value = null;

return false;
}
}
}

View呈现

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
//View呈现抽象类
namespace CHCMVC.View
{
/// <summary>
/// 响应输出
/// </summary>
public abstract class ActionResult
{
public abstract void ExecuteResult(ControllerContext context);
}
}

//View呈现具体实现
namespace CHCMVC.View
{
public class RawContentResult : ActionResult
{
public string RowData { get; set; }

public RawContentResult(string rowData)
{
this.RowData = rowData;
}

/// <summary>
/// 将内容输出到页面上
/// </summary>
/// <param name="context"></param>
public override void ExecuteResult(ControllerContext context)
{
context.RequestContext.HttpContext.Response.Write(this.RowData);
}
}
}

发起请求

请求Model

1
2
3
4
5
6
7
8
namespace CHCMVC.Model
{
public class SimpleModel
{
public string Controller { get; set; }
public string Action { get; set; }
}
}

请求Controller

1
2
3
4
5
6
7
8
9
10
11
namespace CHCMVC.Controller
{
public class HomeController : ControllerBase
{
public ActionResult Index(SimpleModel model)
{
string content = string.Format("Controller: {0}<br/>Action:{1}", model.Controller, model.Action);
return new RawContentResult(content);
}
}
}

请求地址

http://localhost:9077/Home/Index

请求结果

Controller: Home
Action:Index

× 请我吃糖~
打赏二维码