Override GenericRoute(ProductDetails) from Plugin For NopCommerce4.1 And 4.0 .

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
5 years ago
By following below 4 steps I can override the GenericRoute for ProductDetails from my plugin. By following same steps I can override others GenericRoutes. For other generic route overrides I need to replace the given controller name and action name by my plugin controller name and action name.

For nopcommerce4.0 you need to comment out some lines of code from the bellow "RouteAsync" method and also need to uncomment out some code from the same method.

Please change the namespace by your plugin namespace. I put these three class under "PluginRoute" folder of the plugin.


Step1:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Nop.Core.Infrastructure;
using Nop.Web.Framework.Localization;
using Nop.Web.Framework.Mvc.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

/// <summary>
/// Please Replace Bellow NameSpace By Your Plugin
/// </summary>

namespace Nop.Plugin.Widgets.NivoSlider.PluginRoute
{
    public class PGRoute : IRouteProvider
    {
        public int Priority => int.MaxValue;

        public void RegisterRoutes(IRouteBuilder routeBuilder)
        {

            routeBuilder.CustomMapGenericPathRoute("CustomProductDetailGenericRoute", "{SeName}",
              new { controller = "ProductPG", action = "ProductDetails" });
        }
    }
}





Step2:





using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Template;
using Nop.Core;
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 Nop.Core.Data;

/// <summary>
/// Please Replace Bellow NameSpace By Your Plugin
/// </summary>
///

namespace Nop.Plugin.Widgets.NivoSlider.PluginRoute
{
    /// <summary>
    /// Provides properties and methods for defining a SEO friendly route, and for getting information about the route.
    /// </summary>
    public class CustomGenericPathRoute : 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 CustomGenericPathRoute(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

        protected new 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;
        }
        #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)
        {
            //Uncomment the bellow two lines for nopcommerce4.0
            //if (!DataSettingsHelper.DatabaseIsInstalled())
            //    return Task.CompletedTask;

            //comment the bellow two lines for nopcommerce4.0
            if (!DataSettingsManager.DatabaseIsInstalled)
                return Task.CompletedTask;

            //try to get slug from the route data
            var routeValues = GetRouteValues(context);
            if (!routeValues.TryGetValue("SeName", 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

            
            //Uncomment the bellow three lines for nopcommerce4.0

            //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))
            

            //Comment out the bellow two lines for nopcommerce 4.0

            var slugForCurrentLanguage = urlRecordService.GetSeName(urlRecord.EntityId, urlRecord.EntityName);
            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"] = "ProductPG";//replace the controller name by your one
                    currentRouteData.Values["action"] = "ProductDetails";//replace the action name by your one
                    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:
                  //Uncomment the bellow line

                    //EngineContext.Current.Resolve<IEventPublisher>().Publish(new CustomUrlRecordEntityNameRequested(currentRouteData, urlRecord));

                    //Comment out bellow line
                    EngineContext.Current.Resolve<IEventPublisher>().Publish(new CustomUrlRecordEntityNameRequestedEvent(currentRouteData, urlRecord));

                    break;
            }
            context.RouteData = currentRouteData;

            //route request
            return _target.RouteAsync(context);
        }
        #endregion
    }
}







Step3:



using System;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;


/// <summary>
/// Please Replace Bellow NameSpace By Your Plugin
/// </summary>

namespace Nop.Plugin.Widgets.NivoSlider.PluginRoute
{
    /// <summary>
    /// Represents extensions of GenericPathRoute
    /// </summary>
    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 CustomGenericPathRoute(routeBuilder.DefaultHandler, name, template,
                new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(dataTokens),
                inlineConstraintResolver));

            return routeBuilder;
        }
    }
}





Step4:



using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.Orders;
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.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc;
using Nop.Web.Framework.Mvc.Filters;
using Nop.Web.Framework.Mvc.Rss;
using Nop.Web.Framework.Security;
using Nop.Web.Framework.Security.Captcha;


/// <summary>
/// Please Replace Bellow NameSpace By Your Plugin
/// </summary>

namespace Nop.Plugin.Widgets.NivoSlider.Controllers
{
    public partial class ProductPGController : BasePublicController
    {

        #region Product details page
        public virtual IActionResult ProductDetails(int productId, int updatecartitemid = 0)
        {
            // Do you code and return view from your plugin's view folder.
          
            return View("~/Plugins/YourPluginFolderName/Views/CustomeProductDetails.cshtml");



        }
        
        #endregion
    }
}
4 years ago
I am trying your code in version 4.2 and it is not working any idea what changes need to be made?
4 years ago
Please share your code step by step.
3 years ago
has anybody done this for the just release beta 4.3? Seems they move things around and are using SlugRouteTransformer... any ideas on how to overwrite Generic Routes in 4.3
3 years ago
I tried this on version 4.2 and yes it is working. However, if I try and add shopping cart it does not work. Because the urlRecord is null. Any idea why? Is it because I don't have the cart slug in the UrlRecord table?

                    var urlRecord = urlRecordService.GetBySlugCached(slug);


                case "cart":
                    currentRouteData.Values["controller"] = "ShoppingCartPG";
                    currentRouteData.Values["action"] = "GetProductToCartDetails";
                    currentRouteData.Values["topicId"] = urlRecord.EntityId;
                    currentRouteData.Values["SeName"] = urlRecord.Slug;
                    break;


        public void RegisterRoutes(IRouteBuilder routeBuilder)
        {

            routeBuilder.CustomMapGenericPathRoute("CustomProductDetailGenericRoute", "{SeName}",
              new { controller = "ProductPG", action = "ProductDetails" });

            routeBuilder.CustomMapGenericPathRoute("CustomShoppingCartGenericRoute", "{SeName}",
              new { controller = "ShoppingCartPG", action = "GetProductToCartDetails" });

        }
3 years ago
the Shopping cart route does not use slugs....


           //shopping cart
            routeBuilder.MapLocalizedRoute("ShoppingCart", "cart/",
        new { controller = "ShoppingCart", action = "Cart" });      

you would overwrite this route from a route provider from within your plugin


public partial class RouteProvider : IRouteProvider
    {
        /// <summary>
        /// Register routes
        /// </summary>
        /// <param name="routeBuilder">Route builder</param>
        public void RegisterRoutes(IRouteBuilder routeBuilder)
        {
          
        }

        /// <summary>
        /// Gets a priority of route provider
        /// </summary>
        public int Priority
        {
            get { return 0; }
        }
    }
3 years ago
mcselasvegas wrote:
the Shopping cart route does not use slugs....


           //shopping cart
            routeBuilder.MapLocalizedRoute("ShoppingCart", "cart/",
        new { controller = "ShoppingCart", action = "Cart" });      

you would overwrite this route from a route provider from within your plugin


Thanks for replying. It's giving an error.
System.ArgumentException: 'An item with the same key has already been added. Key: ShoppingCart'

I guess I need to remove the previous existing route then add the new one? How do I do this?
3 years ago
give the route a unique name/key....


//shopping cart
            routeBuilder.MapLocalizedRoute("UniqueShoppingCart", "cart/",
        new { controller = "ShoppingCart", action = "Cart" });  
3 years ago
Hi every body.
I want to use of generic Route in my project.if it possible that i have to parameters for Sename,when using of Generic route. for example i want url changed to http://localhost/MyLocalizedCountryName/MyLocalizedProductName.
While here SeName1 is MyLocalizedCountryName and SeName2 is MyLocalizedProductName.
Please Advice me.
Thanks.
3 years ago
mcselasvegas wrote:
the Shopping cart route does not use slugs....


           //shopping cart
            routeBuilder.MapLocalizedRoute("ShoppingCart", "cart/",
        new { controller = "ShoppingCart", action = "Cart" });      

you would overwrite this route from a route provider from within your plugin


public partial class RouteProvider : IRouteProvider
    {
        /// <summary>
        /// Register routes
        /// </summary>
        /// <param name="routeBuilder">Route builder</param>
        public void RegisterRoutes(IRouteBuilder routeBuilder)
        {
          
        }

        /// <summary>
        /// Gets a priority of route provider
        /// </summary>
        public int Priority
        {
            get { return 0; }
        }
    }


Do you know the code for version 4.30? Thank you so much.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.