I use this code for override ProductDetails method in my plugin in nopcommerce 4.0 but is not work in my plugin my code is below step by step :
step 1:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Nop.Web.Framework.Mvc.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nop.Plugin.XcellenceIT.MergeProduct
{
public partial class RouteConfig : IRouteProvider
{
public int Priority => int.MaxValue;
public void RegisterRoutes(IRouteBuilder routeBuilder)
{
routeBuilder.CustomMapGenericPathRoute("CustomProductDetailGenericRoute", "{SeName}",
new { controller = "CustomProduct", action = "ProductDetails" });
}
}
}
step 2:
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Template;
using Nop.Core;
using Nop.Core.Data;
using Nop.Core.Domain.Localization;
using Nop.Core.Infrastructure;
using Nop.Services.Events;
using Nop.Services.Seo;
using Nop.Web.Framework.Localization;
using Nop.Web.Framework.Seo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nop.Plugin.XcellenceIT.MergeProduct
{
public class CustomGenericUrlRouteProvider : GenericPathRoute
{
#region Fields
private readonly IRouter _target;
#endregion
#region Ctor
/// <summary>
/// Ctor
/// </summary>
/// <param name="target">Target</param>
/// <param name="routeName">Route name</param>
/// <param name="routeTemplate">Route remplate</param>
/// <param name="defaults">Defaults</param>
/// <param name="constraints">Constraints</param>
/// <param name="dataTokens">Data tokens</param>
/// <param name="inlineConstraintResolver">Inline constraint resolver</param>
public CustomGenericUrlRouteProvider(IRouter target, string routeName, string routeTemplate, RouteValueDictionary defaults,
IDictionary<string, object> constraints, RouteValueDictionary dataTokens, IInlineConstraintResolver inlineConstraintResolver)
: base(target, routeName, routeTemplate, defaults, constraints, dataTokens, inlineConstraintResolver)
{
_target = target ?? throw new ArgumentNullException(nameof(target));
}
#endregion
#region Utilities
/// <summary>
/// Get route values for current route
/// </summary>
/// <param name="context">Route context</param>
/// <returns>Route values</returns>
protected RouteValueDictionary GetRouteValues(RouteContext context)
{
//remove language code from the path if it's localized URL
var path = context.HttpContext.Request.Path.Value;
if (this.SeoFriendlyUrlsForLanguagesEnabled && path.IsLocalizedUrl(context.HttpContext.Request.PathBase, false, out Language _))
path = path.RemoveLanguageSeoCodeFromUrl(context.HttpContext.Request.PathBase, false);
//parse route data
var routeValues = new RouteValueDictionary(this.ParsedTemplate.Parameters
.Where(parameter => parameter.DefaultValue != null)
.ToDictionary(parameter => parameter.Name, parameter => parameter.DefaultValue));
var matcher = new TemplateMatcher(this.ParsedTemplate, routeValues);
matcher.TryMatch(path, routeValues);
return routeValues;
}
#endregion
#region Methods
/// <summary>
/// Route request to the particular action
/// </summary>
/// <param name="context">A route context object</param>
/// <returns>Task of the routing</returns>
public override Task RouteAsync(RouteContext context)
{
if (!DataSettingsHelper.DatabaseIsInstalled())
return Task.CompletedTask;
//try to get slug from the route data
var routeValues = GetRouteValues(context);
if (!routeValues.TryGetValue("GenericSeName", out object slugValue) || string.IsNullOrEmpty(slugValue as string))
return Task.CompletedTask;
var slug = slugValue as string;
//performance optimization, we load a cached verion here. It reduces number of SQL requests for each page load
var urlRecordService = EngineContext.Current.Resolve<IUrlRecordService>();
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);
//no URL record found
if (urlRecord == null)
return Task.CompletedTask;
//virtual directory path
var pathBase = context.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 Task.CompletedTask;
//redirect to active slug if found
var redirectionRouteData = new RouteData(context.RouteData);
redirectionRouteData.Values["controller"] = "Common";
redirectionRouteData.Values["action"] = "InternalRedirect";
redirectionRouteData.Values["url"] = $"{pathBase}/{activeSlug}{context.HttpContext.Request.QueryString}";
redirectionRouteData.Values["permanentRedirect"] = true;
context.HttpContext.Items["nop.RedirectFromGenericPathRoute"] = true;
context.RouteData = redirectionRouteData;
return _target.RouteAsync(context);
}
//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 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
var redirectionRouteData = new RouteData(context.RouteData);
redirectionRouteData.Values["controller"] = "Common";
redirectionRouteData.Values["action"] = "InternalRedirect";
redirectionRouteData.Values["url"] = $"{pathBase}/{slugForCurrentLanguage}{context.HttpContext.Request.QueryString}";
redirectionRouteData.Values["permanentRedirect"] = false;
context.HttpContext.Items["nop.RedirectFromGenericPathRoute"] = true;
context.RouteData = redirectionRouteData;
return _target.RouteAsync(context);
}
//since we are here, all is ok with the slug, so process URL
var currentRouteData = new RouteData(context.RouteData);
switch (urlRecord.EntityName.ToLowerInvariant())
{
case "product":
currentRouteData.Values["controller"] = "CustomProduct";
currentRouteData.Values["action"] = "ProductDetails";
currentRouteData.Values["productid"] = urlRecord.EntityId;
currentRouteData.Values["SeName"] = urlRecord.Slug;
break;
case "category":
currentRouteData.Values["controller"] = "Catalog";
currentRouteData.Values["action"] = "Category";
currentRouteData.Values["categoryid"] = urlRecord.EntityId;
currentRouteData.Values["SeName"] = urlRecord.Slug;
break;
case "manufacturer":
currentRouteData.Values["controller"] = "Catalog";
currentRouteData.Values["action"] = "Manufacturer";
currentRouteData.Values["manufacturerid"] = urlRecord.EntityId;
currentRouteData.Values["SeName"] = urlRecord.Slug;
break;
case "vendor":
currentRouteData.Values["controller"] = "Catalog";
currentRouteData.Values["action"] = "Vendor";
currentRouteData.Values["vendorid"] = urlRecord.EntityId;
currentRouteData.Values["SeName"] = urlRecord.Slug;
break;
case "newsitem":
currentRouteData.Values["controller"] = "News";
currentRouteData.Values["action"] = "NewsItem";
currentRouteData.Values["newsItemId"] = urlRecord.EntityId;
currentRouteData.Values["SeName"] = urlRecord.Slug;
break;
case "blogpost":
currentRouteData.Values["controller"] = "Blog";
currentRouteData.Values["action"] = "BlogPost";
currentRouteData.Values["blogPostId"] = urlRecord.EntityId;
currentRouteData.Values["SeName"] = urlRecord.Slug;
break;
case "topic":
currentRouteData.Values["controller"] = "Topic";
currentRouteData.Values["action"] = "TopicDetails";
currentRouteData.Values["topicId"] = urlRecord.EntityId;
currentRouteData.Values["SeName"] = urlRecord.Slug;
break;
default:
//no record found, thus generate an event this way developers could insert their own types
EngineContext.Current.Resolve<IEventPublisher>().Publish(new CustomUrlRecordEntityNameRequested(currentRouteData, urlRecord));
break;
}
context.RouteData = currentRouteData;
//route request
return _target.RouteAsync(context);
}
#endregion
}
}
step 3:
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nop.Plugin.XcellenceIT.MergeProduct
{
public static class CustomGenericPathRouteExtensions
{
/// <summary>
/// Adds a route to the route builder with the specified name and template
/// </summary>
/// <param name="routeBuilder">The route builder to add the route to</param>
/// <param name="name">The name of the route</param>
/// <param name="template">The URL pattern of the route</param>
/// <returns>Route builder</returns>
public static IRouteBuilder CustomMapGenericPathRoute(this IRouteBuilder routeBuilder, string name, string template)
{
return CustomMapGenericPathRoute(routeBuilder, name, template, defaults: null);
}
/// <summary>
/// Adds a route to the route builder with the specified name, template, and default values
/// </summary>
/// <param name="routeBuilder">The route builder to add the route to</param>
/// <param name="name">The name of the route</param>
/// <param name="template">The URL pattern of the route</param>
/// <param name="defaults">An object that contains default values for route parameters.
/// The object's properties represent the names and values of the default values</param>
/// <returns>Route builder</returns>
public static IRouteBuilder CustomMapGenericPathRoute(this IRouteBuilder routeBuilder, string name, string template, object defaults)
{
return CustomMapGenericPathRoute(routeBuilder, name, template, defaults, constraints: null);
}
/// <summary>
/// Adds a route to the route builder with the specified name, template, default values, and constraints.
/// </summary>
/// <param name="routeBuilder">The route builder to add the route to</param>
/// <param name="name">The name of the route</param>
/// <param name="template">The URL pattern of the route</param>
/// <param name="defaults"> An object that contains default values for route parameters.
/// The object's properties represent the names and values of the default values</param>
/// <param name="constraints">An object that contains constraints for the route.
/// The object's properties represent the names and values of the constraints</param>
/// <returns>Route builder</returns>
public static IRouteBuilder CustomMapGenericPathRoute(this IRouteBuilder routeBuilder,
string name, string template, object defaults, object constraints)
{
return CustomMapGenericPathRoute(routeBuilder, name, template, defaults, constraints, dataTokens: null);
}
/// <summary>
/// Adds a route to the route builder with the specified name, template, default values, constraints and data tokens.
/// </summary>
/// <param name="routeBuilder">The route builder to add the route to</param>
/// <param name="name">The name of the route</param>
/// <param name="template">The URL pattern of the route</param>
/// <param name="defaults"> An object that contains default values for route parameters.
/// The object's properties represent the names and values of the default values</param>
/// <param name="constraints">An object that contains constraints for the route.
/// The object's properties represent the names and values of the constraints</param>
/// <param name="dataTokens">An object that contains data tokens for the route.
/// The object's properties represent the names and values of the data tokens</param>
/// <returns>Route builder</returns>
public static IRouteBuilder CustomMapGenericPathRoute(this IRouteBuilder routeBuilder,
string name, string template, object defaults, object constraints, object dataTokens)
{
if (routeBuilder.DefaultHandler == null)
throw new ArgumentNullException(nameof(routeBuilder));
//get registered InlineConstraintResolver
var inlineConstraintResolver = routeBuilder.ServiceProvider.GetRequiredService<IInlineConstraintResolver>();
//create new generic route
routeBuilder.Routes.Add(new CustomGenericUrlRouteProvider(routeBuilder.DefaultHandler, name, template,
new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(dataTokens),
inlineConstraintResolver));
return routeBuilder;
}
}
}
step 4:
using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.Orders;
using Nop.Plugin.XcellenceIT.MergeProduct.Services;
using Nop.Services.Catalog;
using Nop.Services.Events;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Security;
using Nop.Services.Seo;
using Nop.Services.Stores;
using Nop.Web.Controllers;
using Nop.Web.Factories;
using Nop.Web.Framework;
using Nop.Web.Framework.Mvc.Filters;
using Nop.Web.Framework.Security;
using Nop.Web.Framework.Security.Captcha;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nop.Plugin.XcellenceIT.MergeProduct.Controllers
{
public partial class CustomProductController : BasePublicController
{
#region Fields
private readonly IProductModelFactory _productModelFactory;
private readonly IProductService _productService;
private readonly IWorkContext _workContext;
private readonly IStoreContext _storeContext;
private readonly ILocalizationService _localizationService;
private readonly IWebHelper _webHelper;
private readonly IRecentlyViewedProductsService _recentlyViewedProductsService;
private readonly ICompareProductsService _compareProductsService;
private readonly IWorkflowMessageService _workflowMessageService;
private readonly IOrderService _orderService;
private readonly IAclService _aclService;
private readonly IStoreMappingService _storeMappingService;
private readonly IPermissionService _permissionService;
private readonly ICustomerActivityService _customerActivityService;
private readonly IEventPublisher _eventPublisher;
private readonly CatalogSettings _catalogSettings;
private readonly ShoppingCartSettings _shoppingCartSettings;
private readonly LocalizationSettings _localizationSettings;
private readonly CaptchaSettings _captchaSettings;
private readonly ICustomProductModelFactory _customProductModelFactory;
#endregion
#region Ctor
public CustomProductController(IProductModelFactory productModelFactory,
IProductService productService,
IWorkContext workContext,
IStoreContext storeContext,
ILocalizationService localizationService,
IWebHelper webHelper,
IRecentlyViewedProductsService recentlyViewedProductsService,
ICompareProductsService compareProductsService,
IWorkflowMessageService workflowMessageService,
IOrderService orderService,
IAclService aclService,
IStoreMappingService storeMappingService,
IPermissionService permissionService,
ICustomerActivityService customerActivityService,
IEventPublisher eventPublisher,
CatalogSettings catalogSettings,
ShoppingCartSettings shoppingCartSettings,
LocalizationSettings localizationSettings,
CaptchaSettings captchaSettings,
ICustomProductModelFactory customProductModelFactory)
{
this._productModelFactory = productModelFactory;
this._productService = productService;
this._workContext = workContext;
this._storeContext = storeContext;
this._localizationService = localizationService;
this._webHelper = webHelper;
this._recentlyViewedProductsService = recentlyViewedProductsService;
this._compareProductsService = compareProductsService;
this._workflowMessageService = workflowMessageService;
this._orderService = orderService;
this._aclService = aclService;
this._storeMappingService = storeMappingService;
this._permissionService = permissionService;
this._customerActivityService = customerActivityService;
this._eventPublisher = eventPublisher;
this._catalogSettings = catalogSettings;
this._shoppingCartSettings = shoppingCartSettings;
this._localizationSettings = localizationSettings;
this._captchaSettings = captchaSettings;
this._customProductModelFactory = customProductModelFactory;
}
#endregion
#region Product details page
[HttpsRequirement(SslRequirement.No)]
public virtual IActionResult ProductDetails(int productId, int updatecartitemid = 0)
{
var product = _productService.GetProductById(productId);
if (product == null || product.Deleted)
return InvokeHttp404();
var notAvailable =
//published?
(!product.Published && !_catalogSettings.AllowViewUnpublishedProductPage) ||
//ACL (access control list)
!_aclService.Authorize(product) ||
//Store mapping
!_storeMappingService.Authorize(product) ||
//availability dates
!product.IsAvailable();
//Check whether the current user has a "Manage products" permission (usually a store owner)
//We should allows him (her) to use "Preview" functionality
if (notAvailable && !_permissionService.Authorize(StandardPermissionProvider.ManageProducts))
return InvokeHttp404();
//visible individually?
if (!product.VisibleIndividually)
{
//is this one an associated products?
var parentGroupedProduct = _productService.GetProductById(product.ParentGroupedProductId);
if (parentGroupedProduct == null)
return RedirectToRoute("HomePage");
return RedirectToRoute("Product", new { SeName = parentGroupedProduct.GetSeName() });
}
//update existing shopping cart or wishlist item?
ShoppingCartItem updatecartitem = null;
if (_shoppingCartSettings.AllowCartItemEditing && updatecartitemid > 0)
{
var cart = _workContext.CurrentCustomer.ShoppingCartItems
.LimitPerStore(_storeContext.CurrentStore.Id)
.ToList();
updatecartitem = cart.FirstOrDefault(x => x.Id == updatecartitemid);
//not found?
if (updatecartitem == null)
{
return RedirectToRoute("Product", new { SeName = product.GetSeName() });
}
//is it this product?
if (product.Id != updatecartitem.ProductId)
{
return RedirectToRoute("Product", new { SeName = product.GetSeName() });
}
}
//save as recently viewed
_recentlyViewedProductsService.AddProductToRecentlyViewedList(product.Id);
//display "edit" (manage) link
if (_permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel) &&
_permissionService.Authorize(StandardPermissionProvider.ManageProducts))
{
//a vendor should have access only to his products
if (_workContext.CurrentVendor == null || _workContext.CurrentVendor.Id == product.VendorId)
{
DisplayEditLink(Url.Action("Edit", "Product", new { id = product.Id, area = AreaNames.Admin }));
}
}
//activity log
_customerActivityService.InsertActivity("PublicStore.ViewProduct", _localizationService.GetResource("ActivityLog.PublicStore.ViewProduct"), product.Name);
//model
var model = _customProductModelFactory.CustomPrepareProductDetailsModel(product, updatecartitem, false);
//template
var productTemplateViewPath = _productModelFactory.PrepareProductTemplateViewPath(product);
return View(productTemplateViewPath, model);
}
#endregion
}
}
- please help me for solve this issue
- still i am not redirect to my custom controller
- thanks for reply