routes - 在 nopcommerce 测试版 4.3 中覆盖通用路由
问题描述
我正在为 nopcommerce beta 4.3 编写一个插件,并试图覆盖一个通用路由(Category/Slug)。该应用程序已移至 .net core 3.x,因此路由现在驻留在中间件中,并且处理方式与以前不同。我试图覆盖 Nop.Web.Infrastructure.GenericUrlRouteProvider 和 Nop.Web.Framework.Mvc.Routing.SlugRouteTransformer 如下...
public partial class GenericUrlRouteProviderExt : IRouteProvider
{
#region Methods
/// <summary>
/// Register routes
/// </summary>
/// <param name="endpointRouteBuilder">Route builder</param>
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
{
var pattern = "{SeName}";
if (DataSettingsManager.DatabaseIsInstalled)
{
var localizationSettings = endpointRouteBuilder.ServiceProvider.GetRequiredService<LocalizationSettings>();
if (localizationSettings.SeoFriendlyUrlsForLanguagesEnabled)
{
var langservice = endpointRouteBuilder.ServiceProvider.GetRequiredService<ILanguageService>();
var languages = langservice.GetAllLanguages().ToList();
pattern = "{language:lang=" + languages.FirstOrDefault().UniqueSeoCode + "}/{SeName}";
}
}
endpointRouteBuilder.MapDynamicControllerRoute<SlugRouteTransformerExt>(pattern);
endpointRouteBuilder.MapControllerRoute("WineCategory", pattern,
new { controller = "Wine", action = "WineCategory" });
}
#endregion
#region Properties
/// <summary>
/// Gets a priority of route provider
/// </summary>
/// <remarks>
/// it should be the last route. we do not set it to -int.MaxValue so it could be overridden (if required)
/// </remarks>
public int Priority => 1000;
/// <summary>
/// Gets the order value of endpoint.
/// </summary>
/// <remarks>
/// The order value provides absolute control over the priority
/// of an endpoint. Endpoints with a lower numeric value of order have higher priority.
/// </remarks>
public int Order => 1;
#endregion
}
和
public class SlugRouteTransformerExt : DynamicRouteValueTransformer
{
private readonly ILanguageService _languageService;
private readonly LocalizationSettings _localizationSettings;
private readonly IUrlRecordService _urlRecordService;
public SlugRouteTransformerExt(ILanguageService languageService,
LocalizationSettings localizationSettings,
IUrlRecordService urlRecordService)
{
_languageService = languageService;
_localizationSettings = localizationSettings;
_urlRecordService = urlRecordService;
}
public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
if (values == null)
return new ValueTask<RouteValueDictionary>(values);
if (!values.TryGetValue("SeName", out var slugValue) || string.IsNullOrEmpty(slugValue as string))
return new ValueTask<RouteValueDictionary>(values);
var slug = slugValue as string;
//performance optimization, we load a cached verion here. It reduces number of SQL requests for each page load
var urlRecord = _urlRecordService.GetBySlug(slug);
//no URL record found
if (urlRecord == null)
return new ValueTask<RouteValueDictionary>(values);
//virtual directory path
var pathBase = httpContext.Request.PathBase;
//if URL record is not active let's find the latest one
if (!urlRecord.IsActive)
{
var activeSlug = _urlRecordService.GetActiveSlug(urlRecord.EntityId, urlRecord.EntityName, urlRecord.LanguageId);
if (string.IsNullOrEmpty(activeSlug))
return new ValueTask<RouteValueDictionary>(values);
//redirect to active slug if found
values[NopPathRouteDefaults.ControllerFieldKey] = "Common";
values[NopPathRouteDefaults.ActionFieldKey] = "InternalRedirect";
values[NopPathRouteDefaults.UrlFieldKey] = $"{pathBase}/{activeSlug}{httpContext.Request.QueryString}";
values[NopPathRouteDefaults.PermanentRedirectFieldKey] = true;
httpContext.Items["nop.RedirectFromGenericPathRoute"] = true;
return new ValueTask<RouteValueDictionary>(values);
}
//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
if (_localizationSettings.SeoFriendlyUrlsForLanguagesEnabled)
{
var urllanguage = values["language"];
if (urllanguage != null && !string.IsNullOrEmpty(urllanguage.ToString()))
{
var language = _languageService.GetAllLanguages().FirstOrDefault(x => x.UniqueSeoCode.ToLowerInvariant() == urllanguage.ToString().ToLowerInvariant());
if (language == null)
language = _languageService.GetAllLanguages().FirstOrDefault();
var slugForCurrentLanguage = _urlRecordService.GetActiveSlug(urlRecord.EntityId, urlRecord.EntityName, language.Id);
if (!string.IsNullOrEmpty(slugForCurrentLanguage) && !slugForCurrentLanguage.Equals(slug, StringComparison.InvariantCultureIgnoreCase))
{
//we should make validation above because some entities does not have SeName for standard (Id = 0) language (e.g. news, blog posts)
//redirect to the page for current language
values[NopPathRouteDefaults.ControllerFieldKey] = "Common";
values[NopPathRouteDefaults.ActionFieldKey] = "InternalRedirect";
values[NopPathRouteDefaults.UrlFieldKey] = $"{pathBase}/{slugForCurrentLanguage}{httpContext.Request.QueryString}";
values[NopPathRouteDefaults.PermanentRedirectFieldKey] = false;
httpContext.Items["nop.RedirectFromGenericPathRoute"] = true;
return new ValueTask<RouteValueDictionary>(values);
}
}
}
//since we are here, all is ok with the slug, so process URL
switch (urlRecord.EntityName.ToLowerInvariant())
{
case "product":
values[NopPathRouteDefaults.ControllerFieldKey] = "Product";
values[NopPathRouteDefaults.ActionFieldKey] = "ProductDetails";
values[NopPathRouteDefaults.ProductIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
break;
case "producttag":
values[NopPathRouteDefaults.ControllerFieldKey] = "Catalog";
values[NopPathRouteDefaults.ActionFieldKey] = "ProductsByTag";
values[NopPathRouteDefaults.ProducttagIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
break;
case "category":
if (!pathBase.ToString().Contains("wine-category"))
{
values[NopPathRouteDefaults.ControllerFieldKey] = "Wine";
values[NopPathRouteDefaults.ActionFieldKey] = "WineCategory";
values[NopPathRouteDefaults.CategoryIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
}
else
{
values[NopPathRouteDefaults.ControllerFieldKey] = "Catalog";
values[NopPathRouteDefaults.ActionFieldKey] = "Category";
values[NopPathRouteDefaults.CategoryIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
}
break;
case "manufacturer":
values[NopPathRouteDefaults.ControllerFieldKey] = "Catalog";
values[NopPathRouteDefaults.ActionFieldKey] = "Manufacturer";
values[NopPathRouteDefaults.ManufacturerIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
break;
case "vendor":
values[NopPathRouteDefaults.ControllerFieldKey] = "Catalog";
values[NopPathRouteDefaults.ActionFieldKey] = "Vendor";
values[NopPathRouteDefaults.VendorIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
break;
case "newsitem":
values[NopPathRouteDefaults.ControllerFieldKey] = "News";
values[NopPathRouteDefaults.ActionFieldKey] = "NewsItem";
values[NopPathRouteDefaults.NewsItemIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
break;
case "blogpost":
values[NopPathRouteDefaults.ControllerFieldKey] = "Blog";
values[NopPathRouteDefaults.ActionFieldKey] = "BlogPost";
values[NopPathRouteDefaults.BlogPostIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
break;
case "topic":
values[NopPathRouteDefaults.ControllerFieldKey] = "Topic";
values[NopPathRouteDefaults.ActionFieldKey] = "TopicDetails";
values[NopPathRouteDefaults.TopicIdFieldKey] = urlRecord.EntityId;
values[NopPathRouteDefaults.SeNameFieldKey] = urlRecord.Slug;
break;
default:
//no record found, thus generate an event this way developers could insert their own types
break;
}
return new ValueTask<RouteValueDictionary>(values);
}
}
但收到错误消息:使用 ExpandEndpoint 要求被替换的端点具有唯一的优先级。发现以下端点具有相同的优先级...
我正在寻找一种有效的方法来拦截指定的路线并重新路由它......
我还考虑在 PluginStratup 中这样做,不知道该找谁...
public void Configure(IApplicationBuilder application)
{
//our custom middleware
application.Use(async (context, next) =>
{
await next();
});
}
有没有人研究过这个并找到了解决方案?
解决方案
我没有阅读您发布的所有代码,但如果我理解正确,您只是想劫持路线。
看一下Nop.Web.Infrastructure.GenericUrlRouteProvider.RegisterRoutes()
,这是定义 category/slug 路线的地方。
您在插件中需要做的就是定义一个实现 IRouteProvider 的类,其优先级大于 -1000000(这是 GenericUrlRouteProvider 使用的优先级)
如:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Nop.Core.Domain.Localization;
using Nop.Data;
using Nop.Services.Localization;
using Nop.Web.Framework.Mvc.Routing;
using System.Linq;
namespace Namespace
{
public class RouteProvider : IRouteProvider
{
/// <summary>
/// Register routes
/// </summary>
/// <param name="endpointRouteBuilder">Route builder</param>
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
{
var pattern = "{SeName}";
if (DataSettingsManager.DatabaseIsInstalled)
{
var localizationSettings = endpointRouteBuilder.ServiceProvider.GetRequiredService<LocalizationSettings>();
if (localizationSettings.SeoFriendlyUrlsForLanguagesEnabled)
{
var langservice = endpointRouteBuilder.ServiceProvider.GetRequiredService<ILanguageService>();
var languages = langservice.GetAllLanguages().ToList();
pattern = "{language:lang=" + languages.FirstOrDefault().UniqueSeoCode + "}/{SeName}";
}
}
endpointRouteBuilder.MapControllerRoute("overriden_Category", pattern, new { controller = "OverridenCatalog", action = "Category" });
}
/// <summary>
/// Gets a priority of route provider
/// </summary>
public int Priority => 0;
}
}
更改它以指向您正确的控制器/动作
推荐阅读
- python - 重命名 Azure Blob 存储中的 csv 文件
- windows - Windows PowerShell - 输入文件名、输出文件路径
- sql-server - 如何通过数据库优先方法更新 asp.net 中的模型,同时保持一些以前的方法有效
- django - 在 Django 中保存以前的日期并添加新日期
- monaco-editor - 在 Monaco 编辑器中启用括号着色
- stm32 - 引脚不想在 PCB STM32 板上变高
- java - Flyway - 自动增量 ID 不适用于 PostgreSQL 中的测试数据
- postgresql - Postgres使用触发器在插入时转换日期格式
- c# - EF Core 1:1 关系?
- ios - 圆形 iOS 按钮在某些设备上变形