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);//查询url对应的路由规则
//comment the line above and uncomment the line below in order to disable this performance "workaround"
//var urlRecord = urlRecordService.GetBySlug(slug);
data.Values["controller"] = "Common";
data.Values["action"] = "PageNotFound";
//ensre that URL record is active
//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))
//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);
data.Values["controller"] = "Common";
data.Values["action"] = "PageNotFound";
//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);
//处理URL并动态赋值真正的Controller的相关信息
switch (urlRecord.EntityName.ToLowerInvariant())
data.Values["controller"] = "Product";
data.Values["action"] = "ProductDetails";
data.Values["productid"] = urlRecord.EntityId;
data.Values["SeName"] = urlRecord.Slug;
data.Values["controller"] = "Catalog";
data.Values["action"] = "Category";
data.Values["categoryid"] = urlRecord.EntityId;
data.Values["SeName"] = urlRecord.Slug;
data.Values["controller"] = "Catalog";
data.Values["action"] = "Manufacturer";
data.Values["manufacturerid"] = urlRecord.EntityId;
data.Values["SeName"] = urlRecord.Slug;
data.Values["controller"] = "Catalog";
data.Values["action"] = "Vendor";
data.Values["vendorid"] = urlRecord.EntityId;
data.Values["SeName"] = urlRecord.Slug;
data.Values["controller"] = "News";
data.Values["action"] = "NewsItem";
data.Values["newsItemId"] = urlRecord.EntityId;
data.Values["SeName"] = urlRecord.Slug;
data.Values["controller"] = "Blog";
data.Values["action"] = "BlogPost";
data.Values["blogPostId"] = urlRecord.EntityId;
data.Values["SeName"] = urlRecord.Slug;
data.Values["controller"] = "Topic";
data.Values["action"] = "TopicDetails";
data.Values["topicId"] = urlRecord.EntityId;
data.Values["SeName"] = urlRecord.Slug;
//generate an event this way developers could insert their own types
EngineContext.Current.Resolve<IEventPublisher>()
.Publish(new CustomUrlRecordEntityNameRequested(data, urlRecord));