你的位置:首页 > ASP.net教程

[ASP.net教程]自定义WebViewPage,实现Url.Action生成绝对地址


前言

运营部门一直对公司官网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