NopCommerce Url分析
using System; using System.Web; using System.Web.Routing; using Nop.Core; using Nop.Core.Data; using Nop.Core.Infrastructure; using Nop.Services.Events; using Nop.Services.Seo; using Nop.Web.Framework.Localization; namespace Nop.Web.Framework.Seo { /// <summary> /// Provides properties and methods for defining a SEO friendly route, and for getting information about the route. /// </summary> public partial class GenericPathRoute : LocalizedRoute { #region Constructors /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern and handler class. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public GenericPathRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class and default parameter values. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public GenericPathRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) : base(url, defaults, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values and constraints. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param> /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public GenericPathRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) : base(url, defaults, constraints, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values, /// constraints,and custom values. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param> /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param> /// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used to determine whether the route matches a specific URL pattern. The route handler might need these values to process the request.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public GenericPathRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler) { } #endregion #region Methods /// <summary> /// Returns information about the requested route. /// </summary> /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param> /// <returns> /// An object that contains the values from the route definition. /// </returns> public override RouteData GetRouteData(HttpContextBase httpContext) { RouteData data = base.GetRouteData(httpContext); if (data != null && DataSettingsHelper.DatabaseIsInstalled()) { var urlRecordService = EngineContext.Current.Resolve<IUrlRecordService>(); var slug = data.Values["generic_se_name"] as string; //performance optimization. //we load a cached verion here. it reduces number of SQL requests for each page load var urlRecord = urlRecordService.GetBySlugCached(slug); //comment the line above and uncomment the line below in order to disable this performance "workaround" //var urlRecord = urlRecordService.GetBySlug(slug); if (urlRecord == null) { //no URL record found //var webHelper = EngineContext.Current.Resolve<IWebHelper>(); //var response = httpContext.Response; //response.Status = "302 Found"; //response.RedirectLocation = webHelper.GetStoreLocation(false); //response.End(); //return null; data.Values["controller"] = "Common"; data.Values["action"] = "PageNotFound"; return data; } //ensre that URL record is active if (!urlRecord.IsActive) { //URL record is not active. let's find the latest one var activeSlug = urlRecordService.GetActiveSlug(urlRecord.EntityId, urlRecord.EntityName, urlRecord.LanguageId); if (string.IsNullOrWhiteSpace(activeSlug)) { //no active slug found //var webHelper = EngineContext.Current.Resolve<IWebHelper>(); //var response = httpContext.Response; //response.Status = "302 Found"; //response.RedirectLocation = webHelper.GetStoreLocation(false); //response.End(); //return null; data.Values["controller"] = "Common"; data.Values["action"] = "PageNotFound"; return data; } //the active one is found var webHelper = EngineContext.Current.Resolve<IWebHelper>(); var response = httpContext.Response; response.Status = "301 Moved Permanently"; response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(false), activeSlug); response.End(); return null; } //ensure that the slug is the same for the current language //otherwise, it can cause some issues when customers choose a new language but a slug stays the same var workContext = EngineContext.Current.Resolve<IWorkContext>(); var slugForCurrentLanguage = SeoExtensions.GetSeName(urlRecord.EntityId, urlRecord.EntityName, workContext.WorkingLanguage.Id); if (!String.IsNullOrEmpty(slugForCurrentLanguage) && !slugForCurrentLanguage.Equals(slug, StringComparison.InvariantCultureIgnoreCase)) { //we should make not null or "" validation above because some entities does not have SeName for standard (ID=0) language (e.g. news, blog posts) var webHelper = EngineContext.Current.Resolve<IWebHelper>(); var response = httpContext.Response; //response.Status = "302 Found"; response.Status = "302 Moved Temporarily"; response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(false), slugForCurrentLanguage); response.End(); return null; } //process URL switch (urlRecord.EntityName.ToLowerInvariant()) { case "product": { data.Values["controller"] = "Product"; data.Values["action"] = "ProductDetails"; data.Values["productid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "category": { data.Values["controller"] = "Catalog"; data.Values["action"] = "Category"; data.Values["categoryid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "manufacturer": { data.Values["controller"] = "Catalog"; data.Values["action"] = "Manufacturer"; data.Values["manufacturerid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "vendor": { data.Values["controller"] = "Catalog"; data.Values["action"] = "Vendor"; data.Values["vendorid"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "newsitem": { data.Values["controller"] = "News"; data.Values["action"] = "NewsItem"; data.Values["newsItemId"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "blogpost": { data.Values["controller"] = "Blog"; data.Values["action"] = "BlogPost"; data.Values["blogPostId"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; case "topic": { data.Values["controller"] = "Topic"; data.Values["action"] = "TopicDetails"; data.Values["topicId"] = urlRecord.EntityId; data.Values["SeName"] = urlRecord.Slug; } break; default: { //no record found //generate an event this way developers could insert their own types EngineContext.Current.Resolve<IEventPublisher>() .Publish(new CustomUrlRecordEntityNameRequested(data, urlRecord)); } break; } } return data; } #endregion } }
using System.Web; using System.Web.Routing; using Nop.Core.Data; using Nop.Core.Domain.Localization; using Nop.Core.Infrastructure; namespace Nop.Web.Framework.Localization { /// <summary> /// Provides properties and methods for defining a localized route, and for getting information about the localized route. /// </summary> public class LocalizedRoute : Route { #region Fields private bool? _seoFriendlyUrlsForLanguagesEnabled; #endregion #region Constructors /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern and handler class. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public LocalizedRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class and default parameter values. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public LocalizedRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) : base(url, defaults, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values and constraints. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param> /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public LocalizedRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) : base(url, defaults, constraints, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values, /// constraints,and custom values. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param> /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param> /// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used to determine whether the route matches a specific URL pattern. The route handler might need these values to process the request.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public LocalizedRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler) { } #endregion #region Methods /// <summary> /// Returns information about the requested route. /// </summary> /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param> /// <returns> /// An object that contains the values from the route definition. /// </returns> public override RouteData GetRouteData(HttpContextBase httpContext) { if (DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled) { string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath; string applicationPath = httpContext.Request.ApplicationPath; if (virtualPath.IsLocalizedUrl(applicationPath, false)) { //In ASP.NET Development Server, an URL like "http://localhost/Blog.aspx/Categories/BabyFrog" will return //"~/Blog.aspx/Categories/BabyFrog" as AppRelativeCurrentExecutionFilePath. //However, in II6, the AppRelativeCurrentExecutionFilePath is "~/Blog.aspx" //It seems that IIS6 think we're process Blog.aspx page. //So, I'll use RawUrl to re-create an AppRelativeCurrentExecutionFilePath like ASP.NET Development Server. //Question: should we do path rewriting right here? string rawUrl = httpContext.Request.RawUrl; var newVirtualPath = rawUrl.RemoveLanguageSeoCodeFromRawUrl(applicationPath); if (string.IsNullOrEmpty(newVirtualPath)) newVirtualPath = "/"; newVirtualPath = newVirtualPath.RemoveApplicationPathFromRawUrl(applicationPath); newVirtualPath = "~" + newVirtualPath; httpContext.RewritePath(newVirtualPath, true); } } RouteData data = base.GetRouteData(httpContext); return data; } /// <summary> /// Returns information about the URL that is associated with the route. /// </summary> /// <param name="requestContext">An object that encapsulates information about the requested route.</param> /// <param name="values">An object that contains the parameters for a route.</param> /// <returns> /// An object that contains information about the URL that is associated with the route. /// </returns> public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { VirtualPathData data = base.GetVirtualPath(requestContext, values); if (data != null && DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled) { string rawUrl = requestContext.HttpContext.Request.RawUrl; string applicationPath = requestContext.HttpContext.Request.ApplicationPath; if (rawUrl.IsLocalizedUrl(applicationPath, true)) { data.VirtualPath = string.Concat(rawUrl.GetLanguageSeoCodeFromUrl(applicationPath, true), "/", data.VirtualPath); } } return data; } public virtual void ClearSeoFriendlyUrlsCachedValue() { _seoFriendlyUrlsForLanguagesEnabled = null; } #endregion #region Properties protected bool SeoFriendlyUrlsForLanguagesEnabled { get { if (!_seoFriendlyUrlsForLanguagesEnabled.HasValue) _seoFriendlyUrlsForLanguagesEnabled = EngineContext.Current.Resolve<LocalizationSettings>().SeoFriendlyUrlsForLanguagesEnabled; return _seoFriendlyUrlsForLanguagesEnabled.Value; } } #endregion } }
using System.Runtime.CompilerServices; namespace System.Web.Routing { // // 摘要: // 提供用于定义路由及获取路由相关信息的属性和方法。 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] public class Route : RouteBase { // // 摘要: // 使用指定的 URL 模式和处理程序类初始化 System.Web.Routing.Route 类的新实例。 // // 参数: // url: // 路由的 URL 模式。 // // routeHandler: // 处理路由请求的对象。 public Route(string url, IRouteHandler routeHandler); // // 摘要: // 使用指定的 URL 模式、默认参数值和处理程序类初始化 System.Web.Routing.Route 类的新实例。 // // 参数: // url: // 路由的 URL 模式。 // // defaults: // 用于 URL 中缺失的任何参数的值。 // // routeHandler: // 处理路由请求的对象。 public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler); // // 摘要: // 使用指定的 URL 模式、默认参数值、约束和处理程序类初始化 System.Web.Routing.Route 类的新实例。 // // 参数: // url: // 路由的 URL 模式。 // // defaults: // 要在 URL 不包含所有参数时使用的值。 // // constraints: // 一个用于指定 URL 参数的有效值的正则表达式。 // // routeHandler: // 处理路由请求的对象。 public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler); // // 摘要: // 使用指定的 URL 模式、默认参数值、约束、自定义值和处理程序类初始化 System.Web.Routing.Route 类的新实例。 // // 参数: // url: // 路由的 URL 模式。 // // defaults: // 要在 URL 不包含所有参数时使用的值。 // // constraints: // 一个用于指定 URL 参数的有效值的正则表达式。 // // dataTokens: // 传递到路由处理程序但未用于确定该路由是否匹配特定 URL 模式的自定义值。 这些值会传递到路由处理程序,以便用于处理请求。 // // routeHandler: // 处理路由请求的对象。 public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler); // // 摘要: // 获取或设置为 URL 参数指定有效值的表达式的词典。 // // 返回结果: // 一个包含参数名称和表达式的对象。 public RouteValueDictionary Constraints { get; set; } // // 摘要: // 获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值。 // // 返回结果: // 一个包含自定义值的对象。 public RouteValueDictionary DataTokens { get; set; } // // 摘要: // 获取或设置要在 URL 不包含所有参数时使用的值。 // // 返回结果: // 一个包含参数名称和默认值的对象。 public RouteValueDictionary Defaults { get; set; } // // 摘要: // 获取或设置处理路由请求的对象。 // // 返回结果: // 处理请求的对象。 public IRouteHandler RouteHandler { get; set; } // // 摘要: // 获取或设置路由的 URL 模式。 // // 返回结果: // 用于匹配路由和 URL 的模式。 // // 异常: // T:System.ArgumentException: // 以下任一值: 以 ~ 或 / 开头的值。 包含 ? 字符的值。 “全部捕捉”参数不在末尾。 // // T:System.Exception: // 没有使用分隔符或文字常量分隔 URL 分段。 public string Url { get; set; } // // 摘要: // 返回有关所请求路由的信息。 // // 参数: // httpContext: // 一个对象,封装有关 HTTP 请求的信息。 // // 返回结果: // 一个对象,其中包含路由定义中的值。 public override RouteData GetRouteData(HttpContextBase httpContext); // // 摘要: // 返回与路由关联的 URL 的相关信息。 // // 参数: // requestContext: // 一个对象,封装有关所请求的路由的信息。 // // values: // 一个包含路由参数的对象。 // // 返回结果: // 一个包含与路由关联的 URL 的相关信息的对象。 public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values); // // 摘要: // 确定参数值是否与该参数的约束匹配。 // // 参数: // httpContext: // 一个对象,封装有关 HTTP 请求的信息。 // // constraint: // 用于测试 parameterName 的正则表达式或对象。 // // parameterName: // 要测试的参数的名称。 // // values: // 要测试的值。 // // routeDirection: // 一个指定 URL 路由是否处理传入请求或构造 URL 的值。 // // 返回结果: // 如果参数值与约束匹配,则为 true;否则为 false。 // // 异常: // T:System.InvalidOperationException: // constraint 不是包含正则表达式的字符串。 protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection); } }
using System; using System.Web.Mvc; using System.Web.Routing; namespace Nop.Web.Framework.Seo { public static class GenericPathRouteExtensions { //Override for localized route public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url) { return MapGenericPathRoute(routes, name, url, null /* defaults */, (object)null /* constraints */); } public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, object defaults) { return MapGenericPathRoute(routes, name, url, defaults, (object)null /* constraints */); } public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, object defaults, object constraints) { return MapGenericPathRoute(routes, name, url, defaults, constraints, null /* namespaces */); } public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, string[] namespaces) { return MapGenericPathRoute(routes, name, url, null /* defaults */, null /* constraints */, namespaces); } public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces) { return MapGenericPathRoute(routes, name, url, defaults, null /* constraints */, namespaces); } public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } var route = new GenericPathRoute(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; } } }