Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建 MVC模拟(摘自ASP.NET MVC5框架揭秘) MVC项目搭建

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可以在开发过程中可以随时添加对其他框架的支持。

首先可以新建一个空站

Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建
MVC模拟(摘自ASP.NET MVC5框架揭秘)
MVC项目搭建

可以向其中添加aspx文件做web Form开发。也可以随时引入MVC框架。

Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建
MVC模拟(摘自ASP.NET MVC5框架揭秘)
MVC项目搭建

添加单元测试

Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建
MVC模拟(摘自ASP.NET MVC5框架揭秘)
MVC项目搭建

使用模版

Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建
MVC模拟(摘自ASP.NET MVC5框架揭秘)
MVC项目搭建

简洁版创建

Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建
MVC模拟(摘自ASP.NET MVC5框架揭秘)
MVC项目搭建

Asp.Net MVC<三> : ASP.NET MVC 基本原理及项目创建
MVC模拟(摘自ASP.NET MVC5框架揭秘)
MVC项目搭建