• 自定义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             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 }
    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 WebViewPage
    107                 // Need to review what happens if this method gets called in Plan9 pointing at an MVC view
    108                 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 page
    112             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 correctly
    120             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 Writer
    132             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 these
    139             // 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

  • 相关阅读:
    镜像---移除
    镜像--保存于载入
    镜像、docker、容器三者关系
    容器管理
    HBase数据读写流程(1.3.1)
    HBase表的memstore与集群memstore
    HBase预分区方法
    HBase中的TTL与MinVersion的关系
    关于HBase的memstoreFlushSize。
    hbase java api样例(版本1.3.1,新API)
  • 原文地址:https://www.cnblogs.com/lonny/p/CustomWebViewPage-CustomUrlHelper-GenerateAbsoluteAddress.html
Copyright © 2020-2023  润新知