前言
运营部门一直对公司官网SEO有意见,认为做得不好(说得好像运营做不好都是seo似的)。为此两部门老大还闹到CEO那去了。
也因为这事,工作计划终于排上日程。沟通一番后得知有如下几点需求:
1.所有链接都得是绝对地址。比如之前是/about.html,现在得变成http://xxx.com/about.html(你妹啊,这样我本地怎么测试)
2.所有目录最后都加/,没有的定久重定向到加/的。比如之前/xxx ,现在得变成http://xxx.com/xxx/
3.图片必须加alt ,width height 属性
分析编码
前些天图片问题已经改完了,现在重点是链接。因为项目链接大多走的Url.Action,所以自然的想到得从这入手。
其实微软已经为我们提供了这个实现,即 @Url.Action("actionName","controllerName","routeValues","protocol","hostName")
全解决方案搜索Url.Action,一千多处。想到前面优化img标签加alt,才五百多处就花了一天半时间,这肯定是不能接受的。
mvc 不是开源了吗,把源码down下来看看,https://git01.codeplex.com/aspnetwebstack
分析源码定位到两个核心的类 UrlHelper WebViewPage
1 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. 2 3 using System.Diagnostics.CodeAnalysis; 4 using System.Globalization; 5 using System.Web.Mvc.Properties; 6 using System.Web.Routing; 7 using System.Web.WebPages; 8 9 namespace System.Web.Mvc 10 { 11 public class UrlHelper 12 { 13 /// <summary> 14 /// Key used to signify that a route URL generation request should include HTTP routes (e.g. Web API). 15 /// If this key is not specified then no HTTP routes will match. 16 /// </summary> 17 private const string HttpRouteKey = "httproute"; 18 19 /// <summary> 20 /// Initializes a new instance of the <see cref="UrlHelper"/> class. 21 /// </summary> 22 /// <remarks>The default constructor is intended for use by unit testing only.</remarks> 23 public UrlHelper() 24 { 25 } 26 27 public UrlHelper(RequestContext requestContext) 28 : this(requestContext, RouteTable.Routes) 29 { 30 } 31 32 public UrlHelper(RequestContext requestContext, RouteCollection routeCollection) 33 { 34 if (requestContext == null) 35 { 36 throw new ArgumentNullException("requestContext"); 37 } 38 if (routeCollection == null) 39 { 40 throw new ArgumentNullException("routeCollection"); 41 } 42 RequestContext = requestContext; 43 RouteCollection = routeCollection; 44 } 45 46 public RequestContext RequestContext { get; private set; } 47 48 public RouteCollection RouteCollection { get; private set; } 49 50 public virtual string Action() 51 { 52 return RequestContext.HttpContext.Request.RawUrl; 53 } 54 55 public virtual string Action(string actionName) 56 { 57 return GenerateUrl(null /* routeName */, actionName, null, (RouteValueDictionary)null /* routeValues */); 58 } 59 60 public virtual string Action(string actionName, object routeValues) 61 { 62 return GenerateUrl(null /* routeName */, actionName, null /* controllerName */, TypeHelper.ObjectToDictionary(routeValues)); 63 } 64 65 public virtual string Action(string actionName, RouteValueDictionary routeValues) 66 { 67 return GenerateUrl(null /* routeName */, actionName, null /* controllerName */, routeValues); 68 } 69 70 public virtual string Action(string actionName, string controllerName) 71 { 72 return GenerateUrl(null /* routeName */, actionName, controllerName, (RouteValueDictionary)null /* routeValues */); 73 } 74 75 public virtual string Action(string actionName, string controllerName, object routeValues) 76 { 77 return GenerateUrl(null /* routeName */, actionName, controllerName, TypeHelper.ObjectToDictionary(routeValues)); 78 } 79 80 public virtual string Action(string actionName, string controllerName, RouteValueDictionary routeValues) 81 { 82 return GenerateUrl(null /* routeName */, actionName, controllerName, routeValues); 83 } 84 85 public virtual string Action(string actionName, string controllerName, RouteValueDictionary routeValues, string protocol) 86 { 87 return GenerateUrl(null /* routeName */, actionName, controllerName, protocol, null /* hostName */, null /* fragment */, routeValues, RouteCollection, RequestContext, true /* includeImplicitMvcValues */); 88 } 89 90 public virtual string Action(string actionName, string controllerName, object routeValues, string protocol) 91 { 92 return GenerateUrl(null /* routeName */, actionName, controllerName, protocol, null /* hostName */, null /* fragment */, TypeHelper.ObjectToDictionary(routeValues), RouteCollection, RequestContext, true /* includeImplicitMvcValues */); 93 } 94 95 public virtual string Action(string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName) 96 { 97 return GenerateUrl(null /* routeName */, actionName, controllerName, protocol, hostName, null /* fragment */, routeValues, RouteCollection, RequestContext, true /* includeImplicitMvcValues */); 98 } 99 100 public virtual string Content(string contentPath) 101 { 102 return GenerateContentUrl(contentPath, RequestContext.HttpContext); 103 } 104 105 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 106 public static string GenerateContentUrl(string contentPath, HttpContextBase httpContext) 107 { 108 if (String.IsNullOrEmpty(contentPath)) 109 { 110 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentPath"); 111 } 112 113 if (httpContext == null) 114 { 115 throw new ArgumentNullException("httpContext"); 116 } 117 118 if (contentPath[0] == '~') 119 { 120 return UrlUtil.GenerateClientUrl(httpContext, contentPath); 121 } 122 else 123 { 124 return contentPath; 125 } 126 } 127 128 //REVIEW: Should we have an overload that takes Uri? 129 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "Needs to take same parameters as HttpUtility.UrlEncode()")] 130 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")] 131 public virtual string Encode(string url) 132 { 133 return HttpUtility.UrlEncode(url); 134 } 135 136 private string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues) 137 { 138 return GenerateUrl(routeName, actionName, controllerName, routeValues, RouteCollection, RequestContext, true /* includeImplicitMvcValues */); 139 } 140 141 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 142 public static string GenerateUrl(string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) 143 { 144 string url = GenerateUrl(routeName, actionName, controllerName, routeValues, routeCollection, requestContext, includeImplicitMvcValues); 145 146 if (url != null) 147 { 148 if (!String.IsNullOrEmpty(fragment)) 149 { 150 url = url + "#" + fragment; 151 } 152 153 if (!String.IsNullOrEmpty(protocol) || !String.IsNullOrEmpty(hostName)) 154 { 155 Uri requestUrl = requestContext.HttpContext.Request.Url; 156 protocol = (!String.IsNullOrEmpty(protocol)) ? protocol : Uri.UriSchemeHttp; 157 hostName = (!String.IsNullOrEmpty(hostName)) ? hostName : requestUrl.Host; 158 159 string port = String.Empty; 160 string requestProtocol = requestUrl.Scheme; 161 162 if (String.Equals(protocol, requestProtocol, StringComparison.OrdinalIgnoreCase)) 163 { 164 port = requestUrl.IsDefaultPort ? String.Empty : (":" + Convert.ToString(requestUrl.Port, CultureInfo.InvariantCulture)); 165 } 166 167 url = protocol + Uri.SchemeDelimiter + hostName + port + url; 168 } 169 } 170 171 return url; 172 } 173 174 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 175 public static string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) 176 { 177 if (routeCollection == null) 178 { 179 throw new ArgumentNullException("routeCollection"); 180 } 181 182 if (requestContext == null) 183 { 184 throw new ArgumentNullException("requestContext"); 185 } 186 187 RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues); 188 189 VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues); 190 if (vpd == null) 191 { 192 return null; 193 } 194 195 string modifiedUrl = UrlUtil.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath); 196 return modifiedUrl; 197 } 198 199 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Response.Redirect() takes its URI as a string parameter.")] 200 public virtual bool IsLocalUrl(string url) 201 { 202 // TODO this should call the System.Web.dll API once it gets added to the framework and MVC takes a dependency on it. 203 return RequestExtensions.IsUrlLocalToHost(RequestContext.HttpContext.Request, url); 204 } 205 206 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 207 public virtual string RouteUrl(object routeValues) 208 { 209 return RouteUrl(null /* routeName */, routeValues); 210 } 211 212 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 213 public virtual string RouteUrl(RouteValueDictionary routeValues) 214 { 215 return RouteUrl(null /* routeName */, routeValues); 216 } 217 218 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 219 public virtual string RouteUrl(string routeName) 220 { 221 return RouteUrl(routeName, (object)null /* routeValues */); 222 } 223 224 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 225 public virtual string RouteUrl(string routeName, object routeValues) 226 { 227 return RouteUrl(routeName, routeValues, null /* protocol */); 228 } 229 230 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 231 public virtual string RouteUrl(string routeName, RouteValueDictionary routeValues) 232 { 233 return RouteUrl(routeName, routeValues, null /* protocol */, null /* hostName */); 234 } 235 236 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 237 public virtual string RouteUrl(string routeName, object routeValues, string protocol) 238 { 239 return GenerateUrl(routeName, null /* actionName */, null /* controllerName */, protocol, null /* hostName */, null /* fragment */, TypeHelper.ObjectToDictionary(routeValues), RouteCollection, RequestContext, false /* includeImplicitMvcValues */); 240 } 241 242 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 243 public virtual string RouteUrl(string routeName, RouteValueDictionary routeValues, string protocol, string hostName) 244 { 245 return GenerateUrl(routeName, null /* actionName */, null /* controllerName */, protocol, hostName, null /* fragment */, routeValues, RouteCollection, RequestContext, false /* includeImplicitMvcValues */); 246 } 247 248 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 249 public virtual string HttpRouteUrl(string routeName, object routeValues) 250 { 251 return HttpRouteUrl(routeName, TypeHelper.ObjectToDictionary(routeValues)); 252 } 253 254 [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "As the return value will used only for rendering, string return value is more appropriate.")] 255 public virtual string HttpRouteUrl(string routeName, RouteValueDictionary routeValues) 256 { 257 if (routeValues == null) 258 { 259 // If no route values were passed in at all we have to create a new dictionary 260 // so that we can add the extra "httproute" key. 261 routeValues = new RouteValueDictionary(); 262 routeValues.Add(HttpRouteKey, true); 263 } 264 else 265 { 266 // Copy the dictionary to add the extra "httproute" key used by all Web API routes to 267 // disambiguate them from other MVC routes. 268 routeValues = new RouteValueDictionary(routeValues); 269 if (!routeValues.ContainsKey(HttpRouteKey)) 270 { 271 routeValues.Add(HttpRouteKey, true); 272 } 273 } 274 275 return GenerateUrl(routeName, 276 actionName: null, 277 controllerName: null, 278 protocol: null, 279 hostName: null, 280 fragment: null, 281 routeValues: routeValues, 282 routeCollection: RouteCollection, 283 requestContext: RequestContext, 284 includeImplicitMvcValues: false); 285 } 286 } 287 }