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

rufengit 2016-09-09 09:00:09
加精
今天研究一下HtmlHelper,ASP.NET MVC框架有数量众多的Helper,包括内置的和用户自己定义的,这些Helper的存在,极大方便了实际开发。我们最为熟悉的莫过于HtmlHelper.ActionLink方法了,它用来在View中生成<a>标签,也就是超链接。在源码中找到其定义,发现它有很多个重载,我们找到其中比较常用的重载版本,见代码段1。
  public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> 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)。
  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<string, object> 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。
  [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。
  [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)。
  // 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得以生成。
 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

...全文
1547 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
maoni599 2016-09-17
  • 打赏
  • 举报
回复
在代码段5中,进一步跟踪GenerateClientUrlInternal方法到其定义(代码段6),最终URL得以生成。
monxinmonxin0 2016-09-12
  • 打赏
  • 举报
回复
zhenzhenjiajia888 2016-09-12
  • 打赏
  • 举报
回复
学习中
王志威丶 2016-09-10
  • 打赏
  • 举报
回复
插眼 马克,留个脚印
insus 2016-09-10
  • 打赏
  • 举报
回复
关注,研究与学习中......
qq_36105881 2016-09-10
  • 打赏
  • 举报
回复
关注,学习中、、、
joyhen 2016-09-10
  • 打赏
  • 举报
回复
妥妥的
kcgtcdy 2016-09-09
  • 打赏
  • 举报
回复
learning...

62,016

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧