Custom Routing: Hijacking an existing route

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

I have created a custom version of the Catalog/Category action.

In short it looks like this:


namespace Strelli.Nop.Controllers {
    public partial class CatalogController : BasePublicController {
        //ctor and stuff ...

        [NopHttpsRequirement(SslRequirement.No)]
        public ActionResult Category(int categoryId, CatalogPagingFilteringModel command) {
            //custom stuff
        }

    }
}


I tried creating a custom routeprovider to hijack the calls to Catalog/Category like so:


namespace Strelli.Nop.Infrastructure {
    public class StrelliRouteProvider : IRouteProvider {
        public int Priority => 1000;

        public void RegisterRoutes(RouteCollection routes) {
            routes.MapGenericPathRoute(
                "StrelliCategory",
                "{SeName}",
                new { controller = "Catalog", action = "Category" },
                new[] { "Strelli.Nop.Controllers" }
            );
        }
    }
}


But now I get the following error:

The parameters dictionary contains a null entry for parameter 'categoryId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Category(Int32, Nop.Web.Models.Catalog.CatalogPagingFilteringModel)' in 'Strelli.Nop.Controllers.CatalogController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters

What am I missing?
8 years ago
The action "Category" need a category int type categoryId where it is? You need to provide the category Id.
8 years ago
The categoryId should be translated from the URL slug, as it normally is when calling the standard nopCommerce Catalog/Category action (found in Nop.Web.Controllers.CatalogController).


As I understand this is achieved in the Nop.Web.Framework.Seo.GenericPathRoute.GetRouteData function by the following lines of code:


switch (urlRecord.EntityName.ToLowerInvariant())
{
    ...
    case "category":
  {
    data.Values["controller"] = "Catalog";
    data.Values["action"] = "Category";
    data.Values["categoryid"] = urlRecord.EntityId;
    data.Values["SeName"] = urlRecord.Slug;
  }
  break;  
    ...
}


This works fine with the default Catalog controller, so I don't see why it doesn't with my custom one.
8 years ago
A illegal solution

public ActionResult Category(int? categoryId, CatalogPagingFilteringModel command)

then from requested url get the SeName and do your staff.:)
8 years ago
I think both of my solution is not perfect.If you solve the issue please share or the link that may help.

what was wrong about my second answer I do not understand please give me some tips.
8 years ago
sina.islam wrote:
I think both of my solution is not perfect.If you solve the issue please share or the link that may help.

what was wrong about my second answer I do not understand please give me some tips.


Well you didn't really answer the question did you? You just shifted the problem.
Your answer is the equivalent of answering the question: "How can I fit a man into a shirt with no sleeves?", with: "Hack of his arms." It does seem to solve the problem at first sight, but it doesn't show any understanding of the underlying problem. It is not a structural solution.

That being said. I solved the problem by changing the RouteProvider to this:


namespace Strelli.Nop.Infrastructure {
    public class StrelliRouteProvider : IRouteProvider {
        public int Priority => 1000;

        public void RegisterRoutes(RouteCollection routes) {
            routes.MapGenericPathRoute("Strelli.Nop.GenericUrl",
                "{generic_se_name}",
                new { controller = "Common", action = "GenericUrl" },
                new[] { "Strelli.Nop.Controllers" });
        }
    }
}


Which makes sense. Adding a new GenericUrl route before the default GenericUrl route is added by nopCommerce will cause the newly provided namespace (in this case: Strelli.Nop.Controllers) to be scanned for a suitable Controller/Action before the default namespace (Nop.Web.Controllers) is.

If no Action is found, the request will fall through to the default GenericUrl route which will make sure it is directed to the default nopCommerce Controller/Action.

However if a custom route is found in the provided namespace, the GetRouteData in Nop.Web.Framework.Seo.GenericPathRoute will still get called. This will make sure all the expected parameters (categoryId in my case) are provided.

My main mistake was trying to overwrite Catalog/Category route directly by calling:


    routes.MapLocalizedRoute(
        "StrelliCategory",
        "{SeName}",
        new { controller = "Catalog", action = "Category" },
        new[] { "Strelli.Nop.Controllers" }
    );


This results in a LocalizedRoute being added to the RouteCollection, instead of the intended GenericPathRoute, so the correct GetRouteData is never called.

At least, that is how I understand it. Please correct me if I made a mistake in this explanation.

The solution was found here: http://www.dotnetdreamer.net/overriding-nopcommerce-default-localized-routes
8 years ago
Exactly man. I also go through the link you share.

I think

const string NAMESPACE = "Nop.Plugin.Misc.Custom.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 }
        };

    }

it  the main concept.Do you try it at version 3.7?
8 years ago
sina.islam wrote:
Exactly man. I also go through the link you share.

I think

const string NAMESPACE = "Nop.Plugin.Misc.Custom.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 }
        };

    }

it  the main concept.Do you try it at version 3.7?


This part was not necessary in my case as I am not creating a plugin, but rather a one off customization. The solution I provided was created for version 3.7, but I believe the routing system was introduced back in version 2.4 or 2.5. So it should work on any recent nopCommerce version.
4 years ago
Check this :

https://www.nopcommerce.com/boards/t/41173/how-to-override-genericpathroutegetroutedata-in-plugin.aspx
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.