How to Override GenericPathRoute.GetRouteData in Plugin?

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
8 years ago
Hi All,

         I want to override slug for Category

 From this => case "category":
                        {
                            data.Values["controller"] = "Catalog";
                            data.Values["action"] = "Category";
                            data.Values["categoryid"] = urlRecord.EntityId;
                            data.Values["SeName"] = urlRecord.Slug;
                        }
To this =>
case "category":
                        {
                            data.Values["controller"] = "WidgetName";
                            data.Values["action"] = "CategoryNew";
                            data.Values["categoryid"] = urlRecord.EntityId;
                            data.Values["SeName"] = urlRecord.Slug;
                        }

How can i achieve it?


Thanks in Advance.
8 years ago
netrawable wrote:
Hi All,

         I want to override slug for Category

 From this => case "category":
                        {
                            data.Values["controller"] = "Catalog";
                            data.Values["action"] = "Category";
                            data.Values["categoryid"] = urlRecord.EntityId;
                            data.Values["SeName"] = urlRecord.Slug;
                        }
To this =>
case "category":
                        {
                            data.Values["controller"] = "WidgetName";
                            data.Values["action"] = "CategoryNew";
                            data.Values["categoryid"] = urlRecord.EntityId;
                            data.Values["SeName"] = urlRecord.Slug;
                        }

How can i achieve it?


Thanks in Advance.


Hi netrawable,

There is no clean way of doing this as the category pages should be handled by the CatalogController.

I am not sure about your exact use case scenario so I will provide you with a possible solution.

In your plugin you can add an ActionFilter to the Category action of the CatalogController.
This way you can put your custom logic just before the Category action is called or after that (depending on what you need).

Hope this helps!
Boyko
8 years ago
Right. There's no clean way of doing it. But you may try the following approach:

1. Add a new class inherited from GenericPathRoute. Override it "GetRouteData" method (put your custom logic there - specify custom controller and action method name)
2. Create a new IRouteProvider implementation in your plugin. Just ensure that it's invoked after the \Infrastructure\GenericUrlRouteProvider.cs (use "Priority" property)
3. Remove "GenericUrl" route name in this IRouteProvider implementation. And add a new custom one. But do not use default "MapGenericPathRoute" extension method. Create a new one that won't use "GenericPathRoute" class but use your new custom one (created at step 1)
8 years ago
At your plugin.cs class write

   const string NAMESPACE = your name space like "Nop.Plugin.Misc.Test.Controllers";

        public void GetConfigurationRoute(out string actionName, out string controllerName,
            out System.Web.Routing.RouteValueDictionary routeValues)
        {
            actionName = null;
            controllerName = null;
            routeValues = new RouteValueDictionary()
            {
                { "Namespaces", NAMESPACE },
                { "area", null }
            };

        }


At your plugin route write

      routes.MapGenericPathRoute("Plugin.Misc.Test.GenericUrl",
                "{generic_se_name}",
                new { controller = "Catalog", action = "Category" },
                new[] { NAMESPACES });


And add a controller to your plugin and named it CatalogController.


I just implement http://www.dotnetdreamer.net/overriding-nopcommerce-default-localized-routes for CatalogController and it is working good.From public site when Category clicked it catch my plugin controller's action   public ActionResult Category(int categoryId, CatalogPagingFilteringModel command) rather than the default   public ActionResult Category(int categoryId, CatalogPagingFilteringModel command).

I have tested this for nopcommerce 3.7.
8 years ago
Hi,


My problems are :
a. When I clicked on any product it is not redirected to product controller.
b. I have some customized filter options like sort by Author name, sort by product types ( book ,jwellery etc. these are specifications of individual products) for categories. These filter options are checkbox control when I select any check box , I did not get categoryid in return, due to which my action method failed to execute.

I did following things and it works for categories to call my customized action method.

1. I created class: BrowseGenericPathRoute.cs in my plugin

 public class BrowseGenericPathRoute : GenericPathRoute
    {
  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))
                    {
                        //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;
                    }
                    else
                    {
                        //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;
                    }
                }

                //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
                if(urlRecord.EntityName.ToLowerInvariant()== "category")
                {
                  
                            data.Values["controller"] = "WidgetsBrowse";
                            data.Values["action"] = "categoryNew";
                            data.Values["categoryid"] = urlRecord.EntityId;
                            data.Values["SeName"] = urlRecord.Slug;
                    
                
                }
            }
            return data;
        }

}


2. Implemented IRouteProvider
  public class RouteProvider : IRouteProvider
    {
      
        public void RegisterRoutes(RouteCollection routes)
        {          

            routes.CustomGenericPathRoute("Plugin.Misc.Test.GenericUrl",
            "{generic_se_name}",
            new { controller = "Catalog", action = "Category" },
            new[] { "Nop.Plugin.Widgets.WidgetsBrowse.Controllers" });

          
        
        }
        public int Priority
        {
            get
            {
                return -1;
            }
        }
    }


3. I Created class BrowseGenericPathRouteExt.cs  in my plugin to call CustomGenericPathRoute method mentioned in second step.

 public static class BrowseGenericPathRouteExt 
    {
        public static Route CustomGenericPathRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces)
        {
            if (routes == null)
            {
                throw new ArgumentNullException("routes");
            }
            if (url == null)
            {
                throw new ArgumentNullException("url");
            }

            var route = new BrowseGenericPathRoute(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;
        }
    }
8 years ago
@netrawable you have done almost everything(really nice work)  but if anyone copy and past the code it will not work because there is a little bug !!!!!!!, that is

"BrowseGenericPathRoute" class need a constructor.I do not do any thing just add a constructor at "BrowseGenericPathRoute" class. Now everything is working good. If anybody copy and past the code as you describe it will work.But he/she must need to add constructor.

public BrowseGenericPathRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler)
        {
        }
7 years ago
Hello,

I have done the same to load custom Category method instead of default one. I have custom model. But, now I want to load custom method for first store only. For second store I want to load default Category method.

How can I achieve it?

Thanks.
7 years ago
Hello,
I've use this code, with sina's edit.
It correctly change controller name to my "CustomCatalogController" but when return the data, I have a page that tell me a 404:


<!DOCTYPE html>
<html >
<head>
    <title>Your store. Page not found</title>
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <meta name="generator" content="nopCommerce" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

...etc...    


My route are:
routes.CustomGenericPathRoute("Nop.Plugin.[NamePlugin]",
            "{generic_se_name}",
            new { controller = "CustomCatalogController", action = "Category" },
            new[] { "Nop.Plugin.[NamePlugin].Controllers" });


In that controller I have the same code of CatalogController changing something, but if I put a breakpoint not fire.

Thanks
6 years ago
Can anyone help me out i am also facing same problem for overriding this GenericPathRoute in custom plugin.
5 years ago
a.m. wrote:
Right. There's no clean way of doing it. But you may try the following approach:

1. Add a new class inherited from GenericPathRoute. Override it "GetRouteData" method (put your custom logic there - specify custom controller and action method name)
2. Create a new IRouteProvider implementation in your plugin. Just ensure that it's invoked after the \Infrastructure\GenericUrlRouteProvider.cs (use "Priority" property)
3. Remove "GenericUrl" route name in this IRouteProvider implementation. And add a new custom one. But do not use default "MapGenericPathRoute" extension method. Create a new one that won't use "GenericPathRoute" class but use your new custom one (created at step 1)


How do we override 'GetRouteValues' method in 4.1?
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.