您的位置:首页 > 编程语言 > ASP

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

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

public static MvcHtmlStringActionLink(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");
}
returnMvcHtmlString.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 stringGenerateLinkInternal(RequestContext requestContext, RouteCollectionrouteCollection, string linkText, string routeName, string actionName, stringcontrollerName, 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 = newTagBuilder("a")
{
InnerHtml =(!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) :String.Empty
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("href", url);
returntagBuilder.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 = "Asthe return value will used only for rendering, string return value is moreappropriate.")]
public static string GenerateUrl(stringrouteName, string actionName, string controllerName, string protocol, stringhostName, string fragment, RouteValueDictionary routeValues, RouteCollectionrouteCollection, 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 = "Asthe return value will used only for rendering, string return value is moreappropriate.")]
public static string GenerateUrl(stringrouteName, string actionName, string controllerName, RouteValueDictionaryrouteValues, RouteCollection routeCollection, RequestContext requestContext,bool includeImplicitMvcValues)
{
if (routeCollection == null)
{
throw newArgumentNullException("routeCollection");
}

if (requestContext == null)
{
throw newArgumentNullException("requestContext");
}

RouteValueDictionarymergedRouteValues = 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 anapp-relative path or an absolute path for contentPath
public static stringGenerateClientUrl(HttpContextBase httpContext, string contentPath)
{
if(String.IsNullOrEmpty(contentPath))
{
return contentPath;
}

// many of the methods we callinternally 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 callinternally can't handle query strings properly, so tack it on after processing
// the virtual app path and urlrewrites
if (String.IsNullOrEmpty(query))
{
returnGenerateClientUrlInternal(httpContext, contentPath);
}
else
{
returnGenerateClientUrlInternal(httpContext, contentPath) + query;
}
}


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

private static stringGenerateClientUrlInternal(HttpContextBase httpContext, string contentPath)
{
if(String.IsNullOrEmpty(contentPath))
{
return contentPath;
}

// can't callVirtualPathUtility.IsAppRelative since it throws on some inputs
bool isAppRelative = contentPath[0]== '~';
if (isAppRelative)
{
string absoluteContentPath =VirtualPathUtility.ToAbsolute(contentPath,httpContext.Request.ApplicationPath);
returnGenerateClientUrlInternal(httpContext, absoluteContentPath);
}

// we only want to manipulate thepath if URL rewriting is active for this request, else we risk breaking thegenerated URL
bool wasRequestRewritten =_urlRewriterHelper.WasRequestRewritten(httpContext);
if (!wasRequestRewritten)
{
return contentPath;
}

// Since the rawUrl represents whatthe user sees in his browser, it is what we want to use as the base
// of our absolute paths. Forexample, consider mysite.example.com/foo, which is internally
// rewritten tocontent.example.com/mysite/foo. When we want to generate a link to ~/bar, wewant 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  .net mvc asp.net 开源 框架