带你读开源—ASP.NET_MVC(十)

今天研究一下HtmlHelper,ASP.NET MVC框架有数量众多的Helper,包括内置的和用户自己定义的,这些Helper的存在,极大方便了实际开发。我们最为熟悉的莫过于HtmlHelper.ActionLink方法了,它用来在View中生成<a>标签,也就是超链接。在源码中找到其定义,发现它有很多个重载,我们找到其中比较常用的重载版本,见代码段1。

C# code

?

1
2
3
4
5
6
7
8
  public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<stringobject> htmlAttributes)
        {
            if (String.IsNullOrEmpty(linkText))
            {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
            }
            return MvcHtmlString.Create(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null /* routeName */, actionName, controllerName, routeValues, htmlAttributes));
        }

 

代码段 1

从代码段1中可以看出这个方法是HtmlHelper类的一个扩展方法,还可以发现代码段1的核心代码是HtmlHelper.GenerateLink方法,我们跟踪GenerateLink方法到最终实现(代码段2)。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
  private static string GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<stringobject> htmlAttributes, bool includeImplicitMvcValues)
        {
            string url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
            TagBuilder tagBuilder = new TagBuilder("a")
            {
                InnerHtml = (!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : String.Empty
            };
            tagBuilder.MergeAttributes(htmlAttributes);
            tagBuilder.MergeAttribute("href", url);
            return tagBuilder.ToString(TagRenderMode.Normal);
        }

 

代码段 2

在代码段2中,用UrlHelper.GenerateUrl方法得到了<a>标签的href属性,然后利用TagBuilder生成HTML标记。我们从语句【InnerHtml = (!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : String.Empty】可以看出MVC对超链接的内容进行了Html编码,以满足安全方面的考量。下面着重看一下UrlHelper.GenerateUrl方法,见代码段3。

C# code

?

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
  [SuppressMessage("Microsoft.Design""CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
        public static string GenerateUrl(string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues)
        {
            string url = GenerateUrl(routeName, actionName, controllerName, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
            if (url != null)
            {
                if (!String.IsNullOrEmpty(fragment))
                {
                    url = url + "#" + fragment;
                }
                if (!String.IsNullOrEmpty(protocol) || !String.IsNullOrEmpty(hostName))
                {
                    Uri requestUrl = requestContext.HttpContext.Request.Url;
                    protocol = (!String.IsNullOrEmpty(protocol)) ? protocol : Uri.UriSchemeHttp;
                    hostName = (!String.IsNullOrEmpty(hostName)) ? hostName : requestUrl.Host;
                    string port = String.Empty;
                    string requestProtocol = requestUrl.Scheme;
                    if (String.Equals(protocol, requestProtocol, StringComparison.OrdinalIgnoreCase))
                    {
                        port = requestUrl.IsDefaultPort ? String.Empty : (":" + Convert.ToString(requestUrl.Port, CultureInfo.InvariantCulture));
                    }
                    url = protocol + Uri.SchemeDelimiter + hostName + port + url;
                }
            }
            return url;
        }

 

代码段 3

UrlHelper.GenerateUrl方法中,首先用另一个也叫GenerateUrl的重载方法[1]获取url,然后在该url的基础上,附加上“协议”、“主机名”、“端口号”等信息,构成一个完整的URL地址。我们进入上标[1]所示的方法定义(代码段4),发现该段代码首先用RouteValuesHelpers.MergeRouteValues方法得到一个包含路由信息的字典mergedRouteValues,再把该字典作为参数传给routeCollection.GetVirtualPathForArea方法,得到一个VirtualPathData类的对象,最后调用UrlUtil.GenerateClientUrl方法生成用户在浏览器端看到的超链接的URL。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  [SuppressMessage("Microsoft.Design""CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
        public static string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues)
        {
            if (routeCollection == null)
            {
                throw new ArgumentNullException("routeCollection");
            }
            if (requestContext == null)
            {
                throw new ArgumentNullException("requestContext");
            }
            RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues);
            VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues);
            if (vpd == null)
            {
                return null;
            }
            string modifiedUrl = UrlUtil.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath);
            return modifiedUrl;
        }

 

代码段 4

我们进入UrlUtil.GenerateClientUrl方法的定义(代码段5)。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  // this method can accept an app-relative path or an absolute path for contentPath
        public static string GenerateClientUrl(HttpContextBase httpContext, string contentPath)
        {
            if (String.IsNullOrEmpty(contentPath))
            {
                return contentPath;
            }
            // many of the methods we call internally can't handle query strings properly, so just strip it out for
            // the time being
            string query;
            contentPath = StripQuery(contentPath, out query);
            // many of the methods we call internally can't handle query strings properly, so tack it on after processing
            // the virtual app path and url rewrites
            if (String.IsNullOrEmpty(query))
            {
                return GenerateClientUrlInternal(httpContext, contentPath);
            }
            else
            {
                return GenerateClientUrlInternal(httpContext, contentPath) + query;
            }
        }

 

代码段 5

在代码段5中,进一步跟踪GenerateClientUrlInternal方法到其定义(代码段6),最终URL得以生成。

C# code

?

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
 private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath)
        {
            if (String.IsNullOrEmpty(contentPath))
            {
                return contentPath;
            }
            // can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
            bool isAppRelative = contentPath[0] == '~';
            if (isAppRelative)
            {
                string absoluteContentPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
                return GenerateClientUrlInternal(httpContext, absoluteContentPath);
            }
            // we only want to manipulate the path if URL rewriting is active for this request, else we risk breaking the generated URL
            bool wasRequestRewritten = _urlRewriterHelper.WasRequestRewritten(httpContext);
            if (!wasRequestRewritten)
            {
                return contentPath;
            }
            // Since the rawUrl represents what the user sees in his browser, it is what we want to use as the base
            // of our absolute paths. For example, consider mysite.example.com/foo, which is internally
            // rewritten to content.example.com/mysite/foo. When we want to generate a link to ~/bar, we want to
            // base it from / instead of /foo, otherwise the user ends up seeing mysite.example.com/foo/bar,
            // which is incorrect.
            string relativeUrlToDestination = MakeRelative(httpContext.Request.Path, contentPath);
            string absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);
            return absoluteUrlToDestination;
        }

 

代码段 6
xiaotmh
  • 版权声明: 本文源自 CSDN CKAOS, 于7个月前,由整理发表,共 8373字。
  • 原文链接:点此查看原文

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: