- MVC之前的那点事儿系列
- http://www.projky.com/asp.netmvc/5.0/
- http://www.cnblogs.com/greatandforever/archive/2010/04/20/1715914.html
MVC模拟(摘自ASP.NET MVC5框架揭秘)
Asp.net中,通过HttpModule的形式定义拦截器,也就是路由表的调用。路由表解析出相应的Controller类型和Action方法的名称及必要参数。然后依据路由数据和请求上下文,选择特定的HttpHandler,采用反射的机制激活目标Controller,执行相应的方法。
实现IHttpModule创建自定义的拦截器
public class UrlRoutingModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication context) { context.PostResolveRequestCache += OnPostResolveRequestCache; } //调用路由表,依据路由数据切换Handler protected virtual void OnPostResolveRequestCache(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); } }
实现IHttpHandler创建自定义的处理程序
public class MvcHandler : IHttpHandler { public bool IsReusable { get { return false; } } public RequestContext RequestContext { get; private set; } public MvcHandler(RequestContext requestContext) { this.RequestContext = requestContext; } 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); } }
路由表解析URL
public class RouteTable { public static RouteDictionary Routes { get; private set; } static RouteTable() { Routes = new RouteDictionary(); } }
public class RouteDictionary : Dictionary<string, RouteBase> { //路由对象集合,自带遍历匹配路由的方法 public RouteData GetRouteData(HttpContextBase httpContext) { foreach (var route in this.Values) { RouteData routeData = route.GetRouteData(httpContext); if (null != routeData) { return routeData; } } return null; } }
继承RouteBase创建自定义的Route类型
//路由 public class Route : RouteBase { //匹配相应的IHttpHandler public IRouteHandler RouteHandler { get; set; } //路由模版 public string Url { get; set; } //限定的命名空间集合 public IDictionary<string, object> DataTokens { get; set; } public Route() { this.DataTokens = new Dictionary<string, object>(); this.RouteHandler = new MvcRouteHandler(); } //获取路由数据,不匹配则返回null. public override RouteData GetRouteData(HttpContextBase httpContext) { IDictionary<string, object> variables; if (this.Match(httpContext.Request .AppRelativeCurrentExecutionFilePath.Substring(2), out variables)) { //匹配成功 RouteData routeData = new RouteData(); //Controller和Action名称 foreach (var item in variables) { routeData.Values.Add(item.Key, item.Value); } //模版中限定的命名空间集合 foreach (var item in DataTokens) { routeData.DataTokens.Add(item.Key, item.Value); } //IHttpHandler路由 routeData.RouteHandler = this.RouteHandler; return routeData; } return null; } //判别当前路由对象(this)是否匹配,匹配则找到对应的Controller和Action名称 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("{}".ToCharArray()), strArray1[i]); } } return true; } }
路由数据
//由路由解析得到的路由数据 public class RouteData { public IDictionary<string, object> Values { get; private set; } public IDictionary<string, object> DataTokens { get; private set; } public IRouteHandler RouteHandler { get; set; } public RouteBase Route { get; set; } public RouteData() { this.Values = new Dictionary<string, object>(); this.DataTokens = new Dictionary<string, object>(); this.DataTokens.Add("namespaces", new List<string>()); } public string Controller { get { object controllerName = string.Empty; this.Values.TryGetValue("controller", out controllerName); return controllerName.ToString(); } } public string ActionName { get { object actionName = string.Empty; this.Values.TryGetValue("action", out actionName); return actionName.ToString(); } } }
初始化(将自定义的路由对象加入路由表)
public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { RouteTable.Routes.Add("default", new Route { Url = "{controller}/{action}" }); } }
实现IRouteHandler自定义HttpHandler提供机制
public class MvcRouteHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { return new MvcHandler(requestContext); } }
Controller实例的获取
public class ControllerBuilder { private Func<IControllerFactory> factoryThunk; public static ControllerBuilder Current { get; private set; } static ControllerBuilder() { Current = new ControllerBuilder(); } public IControllerFactory GetControllerFactory() { return factoryThunk(); } public void SetControllerFactory(IControllerFactory controllerFactory) { factoryThunk = () => controllerFactory; } }
public class DefaultControllerFactory : IControllerFactory { private static List<Type> controllerTypes = new List<Type>(); static DefaultControllerFactory() { //查找包内所有IController foreach (Assembly assembly in BuildManager.GetReferencedAssemblies()) { foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type))) { controllerTypes.Add(type); } } } public IController CreateController(RequestContext requestContext, string controllerName) { string typeName = controllerName + "Controller"; Type controllerType = controllerTypes.FirstOrDefault(c => string.Compare(typeName, c.Name, true) == 0); if (null == controllerType) { return null; } return (IController)Activator.CreateInstance(controllerType); } }
初始化(添加Controller工厂实例)
public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory()); } }
Action方法的执行
public abstract 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.ActionName; this.ActionInvoker.InvokeAction(context, actionName); } }
public class ControllerActionInvoker : IActionInvoker { public IModelBinder ModelBinder { get; private set; } public ControllerActionInvoker() { this.ModelBinder = new DefaultModelBinder(); } public void InvokeAction(ControllerContext controllerContext, string actionName) { MethodInfo methodInfo = controllerContext.Controller.GetType().GetMethods().First(m => string.Compare(actionName, m.Name, true) == 0); List<object> parameters = new List<object>(); foreach (ParameterInfo parameter in methodInfo.GetParameters()) { parameters.Add(this.ModelBinder.BindModel(controllerContext, parameter.Name, parameter.ParameterType)); } ActionExecutor executor = new ActionExecutor(methodInfo); ActionResult actionResult = (ActionResult)executor.Execute(controllerContext.Controller, parameters.ToArray()); actionResult.ExecuteResult(controllerContext); } }
internal class ActionExecutor { private static Dictionary<MethodInfo, Func<object, object[], object>> executors = new Dictionary<MethodInfo, Func<object, object[], object>>(); private static object syncHelper = new object(); public MethodInfo MethodInfo { get; private set; } public ActionExecutor(MethodInfo methodInfo) { this.MethodInfo = methodInfo; } public object Execute(object target, object[] arguments) { Func<object, object[], object> executor; if (!executors.TryGetValue(this.MethodInfo, out executor)) { lock (syncHelper) { if (!executors.TryGetValue(this.MethodInfo, out executor)) { executor = CreateExecutor(this.MethodInfo); executors[this.MethodInfo] = executor; } } } return executor(target, arguments); } private static Func<object, object[], object> CreateExecutor(MethodInfo methodInfo) { ParameterExpression target = Expression.Parameter(typeof(object), "target"); ParameterExpression arguments = Expression.Parameter(typeof(object[]), "arguments"); List<Expression> parameters = new List<Expression>(); ParameterInfo[] paramInfos = methodInfo.GetParameters(); for (int i = 0; i < paramInfos.Length; i++) { ParameterInfo paramInfo = paramInfos[i]; BinaryExpression getElementByIndex = Expression.ArrayIndex(arguments, Expression.Constant(i)); UnaryExpression convertToParameterType = Expression.Convert(getElementByIndex, paramInfo.ParameterType); parameters.Add(convertToParameterType); } UnaryExpression instanceCast = Expression.Convert(target, methodInfo.ReflectedType); MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameters); UnaryExpression convertToObjectType = Expression.Convert(methodCall, typeof(object)); return Expression.Lambda<Func<object, object[], object>>(convertToObjectType, target, arguments).Compile(); } }
public class RawContentResult : ActionResult { private Action<TextWriter> Callback { get; set; } public RawContentResult(Action<TextWriter> action) { this.Callback = action; } public override void ExecuteResult(ControllerContext context) { this.Callback(context.RequestContext.HttpContext.Response.Output); } }
解析Action的参数
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 controllerContext, string modelName, Type modelType, out object value) { Dictionary<string, object> dataSource = new Dictionary<string, object>(); //数据来源一:HttpContext.Current.Request.Form foreach (string key in HttpContext.Current.Request.Form) { if (dataSource.ContainsKey(key.ToLower())) { continue; } dataSource.Add(key.ToLower(), HttpContext.Current.Request.Form[key]); } //数据来源二:HttpContext.Current.Request.QueryString foreach (string key in HttpContext.Current.Request.QueryString) { if (dataSource.ContainsKey(key.ToLower())) { continue; } dataSource.Add(key.ToLower(), HttpContext.Current.Request.QueryString[key]); } //数据来源三:ControllerContext.RequestContext.RouteData.Values foreach (var item in controllerContext.RequestContext.RouteData.Values) { if (dataSource.ContainsKey(item.Key.ToLower())) { continue; } dataSource.Add(item.Key.ToLower(), controllerContext.RequestContext.RouteData.Values[item.Key]); } //数据来源四:ControllerContext.RequestContext.RouteData.DataTokens foreach (var item in controllerContext.RequestContext.RouteData.DataTokens) { if (dataSource.ContainsKey(item.Key.ToLower())) { continue; } dataSource.Add(item.Key.ToLower(), controllerContext.RequestContext.RouteData.DataTokens[item.Key]); } if (dataSource.TryGetValue(modelName.ToLower(), out value)) { value = Convert.ChangeType(value, modelType); return true; } return false; } }
ActionResult
public class HomeController : ControllerBase { public ActionResult Index(SimpleModel model) { Action<TextWriter> callback = writer => { writer.Write(string.Format("Controller: {0}<br/>Action: {1}<br/><br/>", model.Controller, model.Action)); writer.Write(string.Format("Foo: {0}<br/>Bar: {1}<br/>Baz: {2}", model.Foo, model.Bar, model.Baz)); }; return new RawContentResult(callback); } }
MVC项目搭建
MVC5中只有一种ASP.NET Web Application项目类型。通过NuGet可以在开发过程中可以随时添加对其他框架的支持。
首先可以新建一个空站
可以向其中添加aspx文件做web Form开发。也可以随时引入MVC框架。
添加单元测试