RouteDebugger分析

RouteDebugger的介绍  http://www.cnblogs.com/suizhikuo/archive/2012/07/25/2608889.html

2.RouteDebugger结构,一共有5个文件,将逐一解释每个文件的作用

RouteDebug.cs

using System.Web.Routing;

namespace RouteDebug
{
    public class DebugRoute : Route
    {
        static readonly DebugRoute singleton = new DebugRoute();

        public static DebugRoute Singleton
        {
            get { return singleton; }
        }

        private DebugRoute()
            : base("{*catchall}", new DebugRouteHandler()) { }
    }
}

全局单例的DebugRoute,构造函数传入了DebugRouteHandler。

 

DebugRouteHandler.cs

using System.Web;
using System.Web.Routing;

namespace RouteDebug
{
    public class DebugRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new DebugHttpHandler();
        }
    }
}

核心都在DebugHttpHandler最后再解释。

 

PreApplicationStart.cs

using System;
using System.Configuration;
using System.Web;
using RouteDebug;

[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Start")]

namespace RouteDebug
{
    public class PreApplicationStart
    {
        public static void Start()
        {
            bool enabled = Convert.ToBoolean(ConfigurationManager.AppSettings["RouteDebugger:Enabled"]);
            if (enabled)
            {
                Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(RouteDebuggerHttpModule));
            }
        }
    }
}

项目启动时利用DynamicModuleUtility根据Webconfig配置动态的注册RouteDebuggerHttpModule.

 

RouteDebuggerHttpModule.cs

namespace RouteDebug
{
    public class RouteDebuggerHttpModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.EndRequest += OnEndRequest;
            context.BeginRequest += OnBeginRequest;
        }

        static void OnBeginRequest(object sender, System.EventArgs e)
        {
            if (RouteTable.Routes.Last() != DebugRoute.Singleton)
            {
                RouteTable.Routes.Add(DebugRoute.Singleton);
            }
        }

        static void OnEndRequest(object sender, System.EventArgs e)
        {
            var handler = new DebugHttpHandler();

            handler.ProcessRequest(HttpContext.Current);
        }

        public void Dispose()
        {
        }
    }
}

OnBeginRequest向路由表添加DebugRoute.

OnEndRequest输出RouteDebugger信息.

 

DebugHttpHandler.cs

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Web;
using System.Web.Hosting;
using System.Web.Routing;

namespace RouteDebug
{
    public class DebugHttpHandler : IHttpHandler
    {
        readonly VirtualPathProvider _virtualPathProvider;

        public DebugHttpHandler() : this(null) { }

        public DebugHttpHandler(VirtualPathProvider virtualPathProvider)
        {
            _virtualPathProvider = virtualPathProvider ?? HostingEnvironment.VirtualPathProvider;
        }

        public void ProcessRequest(HttpContext context)
        {
            var request = context.Request;

            if (!IsRoutedRequest(request) || context.Response.ContentType == null || !context.Response.ContentType.Equals("text/html", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            string generatedUrlInfo = string.Empty;
            var requestContext = request.RequestContext;

            if (request.QueryString.Count > 0)
            {
                var rvalues = new RouteValueDictionary();
                foreach (string key in request.QueryString.Keys)
                {
                    if (key != null)
                    {
                        rvalues.Add(key, request.QueryString[key]);
                    }
                }

                var vpd = RouteTable.Routes.GetVirtualPath(requestContext, rvalues);
                if (vpd != null)
                {
                    generatedUrlInfo = "<p><label style=\"font-weight: bold; font-size: 1.1em;\">Generated URL</label>: ";
                    generatedUrlInfo += "<strong style=\"color: #00a;\">" + vpd.VirtualPath + "</strong>";
                    var vpdRoute = vpd.Route as Route;
                    if (vpdRoute != null)
                    {
                        generatedUrlInfo += " using the route \"" + vpdRoute.Url + "\"</p>";
                    }
                }
            }

            const string htmlFormat = @"<html>
<div id=""haackroutedebugger"" style=""background-color: #fff; padding-bottom: 10px;"">
    <style>
        #haackroutedebugger, #haackroutedebugger td, #haackroutedebugger th {{background-color: #fff; font-family: verdana, helvetica, san-serif; font-size: small;}}
        #haackroutedebugger tr.header td, #haackroutedebugger tr.header th {{background-color: #ffc;}}
    </style>
    <hr style=""width: 100%; border: solid 1px #000; margin:0; padding:0;"" />
    <h1 style=""margin: 0; padding: 4px; border-bottom: solid 1px #bbb; padding-left: 10px; font-size: 1.2em; background-color: #ffc;"">Route Debugger</h1>
    <div id=""main"" style=""margin-top:0; padding: 0 10px;"">
        <p style=""font-size: .9em; padding-top:0"">
            Type in a url in the address bar to see which defined routes match it. 
            A {{*catchall}} route is added to the list of routes automatically in 
            case none of your routes match.
        </p>
        <p style=""font-size: .9em;"">
            To generate URLs using routing, supply route values via the query string. example: <code>http://localhost:14230/?id=123</code>
        </p>
        <p><label style=""font-weight: bold; font-size: 1.1em;"">Matched Route</label>: {1}</p>
        {5}
        <div style=""float: left;"">
            <table border=""1"" cellpadding=""3"" cellspacing=""0"" width=""300"">
                <caption style=""font-weight: bold;"">Route Data</caption>
                <tr class=""header""><th>Key</th><th>Value</th></tr>
                {0}
            </table>
        </div>
        <div style=""float: left; margin-left: 10px;"">
            <table border=""1"" cellpadding=""3"" cellspacing=""0"" width=""300"">
                <caption style=""font-weight: bold;"">Data Tokens</caption>
                <tr class=""header""><th>Key</th><th>Value</th></tr>
                {4}
            </table>
        </div>
        <hr style=""clear: both;"" />
        <table border=""1"" cellpadding=""3"" cellspacing=""0"">
            <caption style=""font-weight: bold;"">All Routes</caption>
            <tr class=""header"">
                <th>Matches Current Request</th>
                <th>Url</th>
                <th>Defaults</th>
                <th>Constraints</th>
                <th>DataTokens</th>
            </tr>
            {2}
        </table>
        <hr />
        <h3>Current Request Info</h3>
        <p>
            AppRelativeCurrentExecutionFilePath is the portion of the request that Routing acts on.
        </p>
        <p><strong>AppRelativeCurrentExecutionFilePath</strong>: {3}</p>
    </div>
</div>";
            string routeDataRows = string.Empty;

            var routeData = requestContext.RouteData;
            var routeValues = routeData.Values;
            var matchedRouteBase = routeData.Route;

            string routes = string.Empty;
            using (RouteTable.Routes.GetReadLock())
            {
                foreach (var routeBase in RouteTable.Routes)
                {
                    bool matchesCurrentRequest = (routeBase.GetRouteData(requestContext.HttpContext) != null);
                    string matchText = string.Format(@"<span{0}>{1}</span>", BoolStyle(matchesCurrentRequest), matchesCurrentRequest);
                    string url = "n/a";
                    string defaults = "n/a";
                    string constraints = "n/a";
                    string dataTokens = "n/a";

                    Route route = CastRoute(routeBase);

                    if (route != null)
                    {
                        url = route.Url;
                        defaults = FormatDictionary(route.Defaults);
                        constraints = FormatDictionary(route.Constraints);
                        dataTokens = FormatDictionary(route.DataTokens);
                    }

                    routes += string.Format(@"<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>"
                            , matchText
                            , url
                            , defaults
                            , constraints
                            , dataTokens);
                }
            }

            string matchedRouteUrl = "n/a";

            string dataTokensRows = "";

            if (!(matchedRouteBase is DebugRoute))
            {
                foreach (string key in routeValues.Keys)
                {
                    routeDataRows += string.Format("\t<tr><td>{0}</td><td>{1}&nbsp;</td></tr>", key, routeValues[key]);
                }

                foreach (string key in routeData.DataTokens.Keys)
                {
                    dataTokensRows += string.Format("\t<tr><td>{0}</td><td>{1}&nbsp;</td></tr>", key, routeData.DataTokens[key]);
                }

                var matchedRoute = matchedRouteBase as Route;

                if (matchedRoute != null)
                    matchedRouteUrl = matchedRoute.Url;
            }
            else
            {
                matchedRouteUrl = string.Format("<strong{0}>NO MATCH!</strong>", BoolStyle(false));
            }

            context.Response.Write(string.Format(htmlFormat
                , routeDataRows
                , matchedRouteUrl
                , routes
                , request.AppRelativeCurrentExecutionFilePath
                , dataTokensRows
                , generatedUrlInfo));
        }

        private Route CastRoute(RouteBase routeBase)
        {
            var route = routeBase as Route;
            if (route == null)
            {
                // cheat!
                // TODO: Create an interface for self reporting routes.
                var type = routeBase.GetType();
                var property = type.GetProperty("__DebugRoute", BindingFlags.NonPublic | BindingFlags.Instance);
                if (property != null)
                {
                    route = property.GetValue(routeBase, null) as Route;
                }
            }
            return route;
        }

        private static string FormatDictionary(IDictionary<string, object> values)
        {
            if (values == null)
                return "(null)";

            if (values.Count == 0)
            {
                return "(empty)";
            }

            string display = string.Empty;
            foreach (string key in values.Keys)
            {
                display += string.Format("{0} = {1}, ", key, FormatObject(values[key]));
            }
            if (display.EndsWith(", "))
                display = display.Substring(0, display.Length - 2);
            return display;
        }

        private static string FormatObject(object value)
        {
            if (value == null)
            {
                return "(null)";
            }

            var values = value as object[];
            if (values != null)
            {
                return string.Join(", ", values);
            }

            var dictionaryValues = value as IDictionary<string, object>;
            if (dictionaryValues != null)
            {
                return FormatDictionary(dictionaryValues);
            }

            if (value.GetType().Name == "UrlParameter")
            {
                return "UrlParameter.Optional";
            }

            return value.ToString();
        }

        private static string BoolStyle(bool boolean)
        {
            if (boolean) return " style=\"color: #0c0\"";
            return " style=\"color: #c00\"";
        }

        private bool IsRoutedRequest(HttpRequest request)
        {
            string path = request.AppRelativeCurrentExecutionFilePath;
            if (path != "~/" && (_virtualPathProvider.FileExists(path) || _virtualPathProvider.DirectoryExists(path)))
            {
                return false;
            }
            return true;
        }

        public bool IsReusable
        {
            get { return true; }
        }
    }
}

 

IHttpHandler接口肯定都不陌生,有两个地方会调用到ProcessRequest方法。一是RouteDebuggerHttpModule的EndRequest方法。二是匹配到DebugRoute,DebugRoute加在了路由表的最后,没有匹配到任何的Route的时候就会返回DebugRoute。 所以这个时候就会输出两次ProcessRequest. 这是RouteDebugger的核心,循环RouteTable.Routes判断是否匹并且输出。

posted @ 2013-01-24 16:37  不夜橙  阅读(1599)  评论(1编辑  收藏  举报