前言
运营部门一直对公司官网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 else123 {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 dictionary260 // so that we can add the extra "httproute" key.261 routeValues = new RouteValueDictionary();262 routeValues.Add(HttpRouteKey, true);263 }264 else265 {266 // Copy the dictionary to add the extra "httproute" key used by all Web API routes to267 // 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 }
UrlHelper
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.IO; 6 using System.Web.Mvc.Properties; 7 using System.Web.WebPages; 8 9 namespace System.Web.Mvc 10 { 11 public abstract class WebViewPage : WebPageBase, IViewDataContainer, IViewStartPageChild 12 { 13 private ViewDataDictionary _viewData; 14 private DynamicViewDataDictionary _dynamicViewData; 15 private HttpContextBase _context; 16 private HtmlHelper<object> _html; 17 private AjaxHelper<object> _ajax; 18 19 public override HttpContextBase Context 20 { 21 // REVIEW why are we forced to override this? 22 get { return _context ?? ViewContext.HttpContext; } 23 set { _context = value; } 24 } 25 26 public HtmlHelper<object> Html 27 { 28 get 29 { 30 if (_html == null && ViewContext != null) 31 { 32 _html = new HtmlHelper<object>(ViewContext, this); 33 } 34 return _html; 35 } 36 set 37 { 38 _html = value; 39 } 40 } 41 42 public AjaxHelper<object> Ajax 43 { 44 get 45 { 46 if (_ajax == null && ViewContext != null) 47 { 48 _ajax = new AjaxHelper<object>(ViewContext, this); 49 } 50 return _ajax; 51 } 52 set 53 { 54 _ajax = value; 55 } 56 } 57 58 public object Model 59 { 60 get { return ViewData.Model; } 61 } 62 63 internal string OverridenLayoutPath { get; set; } 64 65 public TempDataDictionary TempData 66 { 67 get { return ViewContext.TempData; } 68 } 69 70 public UrlHelper Url { get; set; } 71 72 public dynamic ViewBag 73 { 74 get 75 { 76 if (_dynamicViewData == null) 77 { 78 _dynamicViewData = new DynamicViewDataDictionary(() => ViewData); 79 } 80 return _dynamicViewData; 81 } 82 } 83 84 public ViewContext ViewContext { get; set; } 85 86 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is the mechanism by which the ViewPage gets its ViewDataDictionary object.")] 87 public ViewDataDictionary ViewData 88 { 89 get 90 { 91 if (_viewData == null) 92 { 93 SetViewData(new ViewDataDictionary()); 94 } 95 return _viewData; 96 } 97 set { SetViewData(value); } 98 } 99 100 protected override void ConfigurePage(WebPageBase parentPage)101 {102 var baseViewPage = parentPage as WebViewPage;103 if (baseViewPage == null)104 {105 // TODO : review if this check is even necessary.106 // When this method is called by the framework parentPage should already be an instance of WebViewPage107 // Need to review what happens if this method gets called in Plan9 pointing at an MVC view108 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.CshtmlView_WrongViewBase, parentPage.VirtualPath));109 }110 111 // Set ViewContext and ViewData here so that the layout page inherits ViewData from the main page112 ViewContext = baseViewPage.ViewContext;113 ViewData = baseViewPage.ViewData;114 InitHelpers();115 }116 117 public override void ExecutePageHierarchy()118 {119 // Change the Writer so that things like Html.BeginForm work correctly120 TextWriter oldWriter = ViewContext.Writer;121 ViewContext.Writer = Output;122 123 base.ExecutePageHierarchy();124 125 // Overwrite LayoutPage so that returning a view with a custom master page works.126 if (!String.IsNullOrEmpty(OverridenLayoutPath))127 {128 Layout = OverridenLayoutPath;129 }130 131 // Restore the old View Context Writer132 ViewContext.Writer = oldWriter;133 }134 135 public virtual void InitHelpers()136 {137 // Html and Ajax helpers are lazily initialized since they are not directly visible to a Razor page.138 // In order to ensure back-compat, in the event that this instance gets re-used, we'll reset these139 // properties so they get reinitialized the very next time they get accessed.140 Html = null;141 Ajax = null;142 Url = new UrlHelper(ViewContext.RequestContext);143 }144 145 protected virtual void SetViewData(ViewDataDictionary viewData)146 {147 _viewData = viewData;148 }149 }150 }
WebViewPage
上文中Url 实际上是UrlHelper 类型 在WebViewPage 中虚方法 InitHelpers 完成实例化。
转到UrlHelper看看,Action有九个重载。而项目中只用到了如下三个:
1 public virtual string Action(string actionName, string controllerName) 2 { 3 return GenerateUrl(null /* routeName */, actionName, controllerName, (RouteValueDictionary)null /* routeValues */); 4 } 5 6 public virtual string Action(string actionName, string controllerName, object routeValues) 7 { 8 return GenerateUrl(null /* routeName */, actionName, controllerName, TypeHelper.ObjectToDictionary(routeValues)); 9 }10 11 public virtual string Action(string actionName, string controllerName, RouteValueDictionary routeValues)12 {13 return GenerateUrl(null /* routeName */, actionName, controllerName, routeValues);14 }
上文有提到一个可以生成绝对地址的重载
1 public virtual string Action(string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName)2 {3 return GenerateUrl(null /* routeName */, actionName, controllerName, protocol, hostName, null /* fragment */, routeValues, RouteCollection, RequestContext, true /* includeImplicitMvcValues */);4 }
也就是说,我只要自定义一个UrlHelper实现项目中重载版本间接调用这个生成绝对地址的重载版本即可。
Url 定义在WebViewPage中,意味着WebViewPage也得自定义。可问题是自定义如何用起来呢?搜索一番找到文章结尾三篇文章从中得到了我要答案。
编译发布。整站都浏览了一遍,只要走Url.Action的都是绝对地址了。 nice!无缝对接啊。
总结:
因为生成的绝对地址随iis域名端口自动变化,不会出现本地开发环境也是生产环境地址没法测试问题。项目几乎没什么变动,只需要修改下配置文件即可,无缝对接!
最终版核心代码
using System;using System.Web.Mvc;using System.Web.WebPages;namespace SF.MVC.Core{ public abstract class WebViewPage<TModel> : System.Web.Mvc.WebViewPage<TModel> { public new CustomUrlHelper Url { get; set; } public override void InitHelpers() { base.InitHelpers(); Url = new CustomUrlHelper(ViewContext.RequestContext); } } public abstract class WebViewPage : WebViewPage<dynamic> { }}
CustomWebViewPage
1 using System; 2 using System.Globalization; 3 using System.Web.Mvc; 4 using System.Web.Routing; 5 using System.Web.WebPages; 6 using System.Collections.Generic; 7 8 namespace SF.MVC.Core 9 { 10 public class CustomUrlHelper : UrlHelper 11 { 12 public CustomUrlHelper(RequestContext requestContext) 13 : base(requestContext, RouteTable.Routes) 14 { 15 } 16 17 public new string Action(string actionName, string controllerName) 18 { 19 var hostName = RequestContext.HttpContext.Request.Url.Host; 20 return GenerateUrl(null, actionName, controllerName, null, hostName, null, (RouteValueDictionary)null, RouteCollection, RequestContext, true); 21 } 22 23 public new string Action(string actionName, string controllerName, object routeValues) 24 { 25 var hostName = RequestContext.HttpContext.Request.Url.Host; 26 return GenerateUrl(null, actionName, controllerName, null, hostName, null, new RouteValueDictionary(routeValues), RouteCollection, RequestContext, true); 27 } 28 29 public new string RouteUrl(string routeName) 30 { 31 var hostName = RequestContext.HttpContext.Request.Url.Host; 32 return GenerateUrl(routeName, null, null, null, hostName, null, (RouteValueDictionary)null, RouteCollection, RequestContext, false); 33 34 } 35 36 public new static string GenerateUrl(string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) 37 { 38 string url = GenerateUrl(routeName, actionName, controllerName, routeValues, routeCollection, requestContext, includeImplicitMvcValues); 39 40 if (url != null) 41 { 42 if (!IsContain(url)) 43 url += "/"; 44 45 if (!String.IsNullOrEmpty(fragment)) 46 { 47 url = url + "#" + fragment; 48 } 49 50 if (!String.IsNullOrEmpty(protocol) || !String.IsNullOrEmpty(hostName)) 51 { 52 Uri requestUrl = requestContext.HttpContext.Request.Url; 53 protocol = (!String.IsNullOrEmpty(protocol)) ? protocol : Uri.UriSchemeHttp; 54 hostName = (!String.IsNullOrEmpty(hostName)) ? hostName : requestUrl.Host; 55 56 string port = String.Empty; 57 string requestProtocol = requestUrl.Scheme; 58 59 if (String.Equals(protocol, requestProtocol, StringComparison.OrdinalIgnoreCase)) 60 { 61 port = requestUrl.IsDefaultPort ? String.Empty : (":" + Convert.ToString(requestUrl.Port, CultureInfo.InvariantCulture)); 62 } 63 64 url = protocol + Uri.SchemeDelimiter + hostName + port + url; 65 } 66 } 67 68 return url; 69 } 70 71 /// <summary> 72 /// 判断字符串中是否包含某部分 73 /// </summary> 74 /// <param name="input"></param> 75 /// <returns></returns> 76 private static bool IsContain(string input) 77 { 78 if (string.IsNullOrWhiteSpace(input)) return false; 79 80 if (input == "/") 81 { 82 return true; 83 } 84 else if(input.Contains("articledetial")) 85 { 86 return true; 87 } 88 else if (input.Contains("special")) 89 { 90 return true; 91 } 92 else if(input.EndsWith(".html", StringComparison.CurrentCultureIgnoreCase)) 93 { 94 return true; 95 } 96 97 return false; 98 } 99 100 }101 }
CustomUrlHelper
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Web.Mvc; 6 7 namespace SF.MVC.Core 8 { 9 public class PermanentRedirectFilter : ActionFilterAttribute 10 { 11 12 /// <summary>13 /// 301永久重定向14 /// </summary>15 /// <param name="filterContext"></param>16 public override void OnActionExecuting(ActionExecutingContext filterContext)17 {18 string url = filterContext.HttpContext.Request.Url.AbsolutePath;19 20 if (!url.EndsWith("/"))21 {22 filterContext.HttpContext.Response.AddHeader("Location", url + "/");23 filterContext.HttpContext.Response.Status = "301 Moved Permanently";24 filterContext.HttpContext.Response.StatusCode = 301;25 26 }27 28 }29 }30 31 }
301永久重定向
Views/Web.config如下节点:
<system.web.webPages.razor> <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <pages pageBaseType="SF.MVC.Core.WebViewPage"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Optimization"/> <add namespace="System.Web.Routing" /> <add namespace="SF.MVC.Core" /> </namespaces> </pages> </system.web.webPages.razor>
View Code
后记:
人算不如天算,本机、测试环境都测试通过了,没想到生产环境做了负载均衡,有端口号反而杯具了。 这样就会出现有时需要有时不需要问题,没办法只能加个配置项来控制了。
1 using System; 2 using System.Globalization; 3 using System.Web.Mvc; 4 using System.Web.Routing; 5 using System.Web.WebPages; 6 using System.Collections.Generic; 7 using MSP.Common.Tools; 8 9 namespace SF.MVC.Core 10 { 11 public class CustomUrlHelper : UrlHelper 12 { 13 /// <summary> 14 /// 是否需要加端口号 15 /// </summary> 16 private static bool IsNeedProtocol = false; 17 18 public CustomUrlHelper(RequestContext requestContext) 19 : base(requestContext, RouteTable.Routes) 20 { 21 ConfigManager config = new ConfigManager(); 22 config.LoadConfig("common"); 23 24 string configValue = config.GetConfig("NeedProtocol") == null ? "false" : config.GetConfig("NeedProtocol").Value; 25 26 bool.TryParse(configValue, out IsNeedProtocol); 27 28 } 29 30 public new string Action(string actionName, string controllerName) 31 { 32 var hostName = RequestContext.HttpContext.Request.Url.Host; 33 return GenerateUrl(null, actionName, controllerName, null, hostName, null, (RouteValueDictionary)null, RouteCollection, RequestContext, true); 34 } 35 36 public new string Action(string actionName, string controllerName, object routeValues) 37 { 38 var hostName = RequestContext.HttpContext.Request.Url.Host; 39 return GenerateUrl(null, actionName, controllerName, null, hostName, null, new RouteValueDictionary(routeValues), RouteCollection, RequestContext, true); 40 } 41 42 public new string RouteUrl(string routeName) 43 { 44 var hostName = RequestContext.HttpContext.Request.Url.Host; 45 return GenerateUrl(routeName, null, null, null, hostName, null, (RouteValueDictionary)null, RouteCollection, RequestContext, false); 46 47 } 48 49 public new static string GenerateUrl(string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) 50 { 51 string url = GenerateUrl(routeName, actionName, controllerName, routeValues, routeCollection, requestContext, includeImplicitMvcValues); 52 53 if (url != null) 54 { 55 if (!IsContain(url)) 56 url += "/"; 57 58 if (!String.IsNullOrEmpty(fragment)) 59 { 60 url = url + "#" + fragment; 61 } 62 63 if (!String.IsNullOrEmpty(protocol) || !String.IsNullOrEmpty(hostName)) 64 { 65 Uri requestUrl = requestContext.HttpContext.Request.Url; 66 protocol = (!String.IsNullOrEmpty(protocol)) ? protocol : Uri.UriSchemeHttp; 67 hostName = (!String.IsNullOrEmpty(hostName)) ? hostName : requestUrl.Host; 68 69 string port = String.Empty; 70 string requestProtocol = requestUrl.Scheme; 71 72 if (IsNeedProtocol && String.Equals(protocol, requestProtocol, StringComparison.OrdinalIgnoreCase)) 73 { 74 port = requestUrl.IsDefaultPort ? String.Empty : (":" + Convert.ToString(requestUrl.Port, CultureInfo.InvariantCulture)); 75 } 76 77 url = protocol + Uri.SchemeDelimiter + hostName + port + url; 78 } 79 } 80 81 return url; 82 } 83 84 /// <summary> 85 /// 判断字符串中是否包含某部分 86 /// </summary> 87 /// <param name="input"></param> 88 /// <returns></returns> 89 private static bool IsContain(string input) 90 { 91 if (string.IsNullOrWhiteSpace(input)) return false; 92 93 if (input == "/") 94 { 95 return true; 96 } 97 else if(input.Contains("articledetial")) 98 { 99 return true;100 }101 else if (input.Contains("special"))102 {103 return true;104 }105 else if(input.EndsWith(".html", StringComparison.CurrentCultureIgnoreCase))106 {107 return true;108 } 109 110 return false;111 }112 113 }114 }
修改后CustomUrlHelper
代码中构造函数读取配置您需要改写成自己的实现逻辑。
参考:
Changing Base Type Of A Razor View
Create Your Own Custom ViewWebPage for ASP.NET MVC
MVC中自定义ViewPage和WebViewPage
原标题:自定义WebViewPage,实现Url.Action生成绝对地址
关键词:web