Overriding other controllers and their routes

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

Currently trying to override the CatalogController's method HomepageProducts in order to return my own partial view in a plugin. I wanted to do this by just overriding its route and make a RouteProvider-class in my plugin-project:


public partial class RouteProvider : IRouteProvider
    {
        public void RegisterRoutes(RouteCollection routes)
        {
            // Homepage products
            routes.MapLocalizedRoute("Nop.Plugin.Product.BoxedProductView.HomepageProducts",
                 "Catalog/HomepageProducts",
                 new { controller = "BoxedProductController", action = "HomepageProducts" },
                 new[] { "Nop.Plugin.Product.BoxedProductView" }
            );
        }

        public int Priority
        {
            get
            {
                return 100;
            }
        }
    }


The route does get added, but when @Html.Action("HomepageProducts", "Catalog") gets called in Index.cshtml in \Views\Home, my plugin-Controller does not get called. Is the above not the correct way to override routes in nopCommerce?
10 years ago
chr2306 wrote:
Hey!

Currently trying to override the CatalogController's method HomepageProducts in order to return my own partial view in a plugin. I wanted to do this by just overriding its route and make a RouteProvider-class in my plugin-project:


public partial class RouteProvider : IRouteProvider
    {
        public void RegisterRoutes(RouteCollection routes)
        {
            // Homepage products
            routes.MapLocalizedRoute("Nop.Plugin.Product.BoxedProductView.HomepageProducts",
                 "Catalog/HomepageProducts",
                 new { controller = "BoxedProductController", action = "HomepageProducts" },
                 new[] { "Nop.Plugin.Product.BoxedProductView" }
            );
        }

        public int Priority
        {
            get
            {
                return 100;
            }
        }
    }


The route does get added, but when @Html.Action("HomepageProducts", "Catalog") gets called in Index.cshtml in \Views\Home, my plugin-Controller does not get called. Is the above not the correct way to override routes in nopCommerce?


The line Html.Action doesn't work by routing, it works by directing calling the HomepageProducts action method of Catalog controller. :)
10 years ago
wooncherk wrote:
The line Html.Action doesn't work by routing, it works by directing calling the HomepageProducts action method of Catalog controller. :)


Does it though? See: http://stackoverflow.com/questions/8935750/why-do-you-need-a-route-defined-for-html-action
10 years ago
Specifically

stackoverflow.com wrote:
Essentially, routing is involved in determining which controller to 'fire up' to handle the request and appropriate action to invoke based on the parameters that you sent and the MVCRouteHandler uses those to make a decision. Just because you tell it which controller in your action does not make it magically ignore the routing table, go straight to that controller class and bypass all the other MVC goodness that happens in the back-end. Remember, these @HTML.Action methods can take a whole lot of overloads which could influence which route in the routing table to use (think URL structure for instance).
10 years ago
chr2306 wrote:
Specifically

Essentially, routing is involved in determining which controller to 'fire up' to handle the request and appropriate action to invoke based on the parameters that you sent and the MVCRouteHandler uses those to make a decision. Just because you tell it which controller in your action does not make it magically ignore the routing table, go straight to that controller class and bypass all the other MVC goodness that happens in the back-end. Remember, these @HTML.Action methods can take a whole lot of overloads which could influence which route in the routing table to use (think URL structure for instance).


Ok, I shall put it in another way.

The fact that you've registered a route with URL 'Catalog/HomepageProducts' doesn't make @Html.Action("HomepageProducts", "Catalog") to take your route. If you visit ~/Catalog/HomepageProducts directly in your browser then yes, your plugin will override the original render. But when you call @Html.Action("HomepageProducts", "Catalog") directly, the route that is actually taking place should be:

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new[] { "Nop.Web.Controllers" }
            );


which is defined in Global.asax.cs. :)
10 years ago
wooncherk wrote:
Ok, I shall put it in another way.

The fact that you've registered a route with URL 'Catalog/HomepageProducts' doesn't make @Html.Action("HomepageProducts", "Catalog") to take your route. If you visit ~/Catalog/HomepageProducts directly in your browser then yes, your plugin will override the original render. But when you call @Html.Action("HomepageProducts", "Catalog") directly, the route that is actually taking place should be:

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new[] { "Nop.Web.Controllers" }
            );


which is defined in Global.asax.cs. :)

Indeed, but according to http://msdn.microsoft.com/en-us/library/cc668201(v=vs.100).aspx:
msdn wrote:
Route matching is tried from the first route to the last route in the collection. When a match occurs, no more routes are evaluated.

And since I'm using Priority = 100 in my RouteProvider it is registered before the "Default"-route (and I verify this by way of debugging). So again, by way of logic, the "Default"-route should only be matched if my route is not matched first. And if @Html.Action does go by route table, then the route values should hit my defined route.

Might it be an issue with area or parameters?
10 years ago
chr2306 wrote:
Ok, I shall put it in another way.

The fact that you've registered a route with URL 'Catalog/HomepageProducts' doesn't make @Html.Action("HomepageProducts", "Catalog") to take your route. If you visit ~/Catalog/HomepageProducts directly in your browser then yes, your plugin will override the original render. But when you call @Html.Action("HomepageProducts", "Catalog") directly, the route that is actually taking place should be:

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new[] { "Nop.Web.Controllers" }
            );


which is defined in Global.asax.cs. :)
Indeed, but according to http://msdn.microsoft.com/en-us/library/cc668201(v=vs.100).aspx:
Route matching is tried from the first route to the last route in the collection. When a match occurs, no more routes are evaluated.
And since I'm using Priority = 100 in my RouteProvider it is registered before the "Default"-route (and I verify this by way of debugging). So again, by way of logic, the "Default"-route should only be matched if my route is not matched first. And if @Html.Action does go by route table, then the route values should hit my defined route.

Might it be an issue with area or parameters?


Ok, let's put it this way. Imagine the URL defined in your route reads 'TestC/TestA' instead of 'Catalog/HomepageProducts', with everything else the same. So it eventually reads:

            
routes.MapLocalizedRoute("Nop.Plugin.Product.BoxedProductView.HomepageProducts",
                 "TestC/TestA",
                 new { controller = "BoxedProductController", action = "HomepageProducts" },
                 new[] { "Nop.Plugin.Product.BoxedProductView" }
            );


Now tell me, which controller and action does this route correspond to? I have a feeling you are confused by the 'Catalog/HomepageProducts' URL part, but how the URL looks like is not indicative of how the @Html.Action("Catalog", "HomepageProducts") code will react.

Essentially your route is mapping to BoxedProductController, HomepageProducts action of Nop.Plugin.Product.BoxedProductView namespace. It is not like just because you format the route URL to look like Catalog/HomepageProducts, then your route will override the default render of Html.Action("Catalog", "HomepageProducts"). It will override if you visit ~/Catalog/HomepageProducts though.

What you can try instead is to use a route like:

            
routes.MapLocalizedRoute("Nop.Plugin.Product.BoxedProductView.HomepageProducts",
                 "TestC/TestA",
                 new { controller = "Catalog", action = "HomepageProducts" },
                 new[] { "Nop.Plugin.Product.BoxedProductView" }
            );


Like this, your route table have 2 entries matching Controller = Catalog and Action = HomepageProducts, but with different namespace. And because your route have a higher priority, it'll be used eventually. But what is making the difference is not the URL, it's the name space. Notice that I am using TestC/TestA as the URL, but it doesn't matter in case of Html.Action(). :)
10 years ago
wooncherk wrote:
Ok, let's put it this way. Imagine the URL defined in your route reads 'TestC/TestA' instead of 'Catalog/HomepageProducts', with everything else the same. So it eventually reads:

            
routes.MapLocalizedRoute("Nop.Plugin.Product.BoxedProductView.HomepageProducts",
                 "TestC/TestA",
                 new { controller = "BoxedProductController", action = "HomepageProducts" },
                 new[] { "Nop.Plugin.Product.BoxedProductView" }
            );


Now tell me, which controller and action does this route correspond to? I have a feeling you are confused by the 'Catalog/HomepageProducts' URL part, but how the URL looks like is not indicative of how the @Html.Action("Catalog", "HomepageProducts") code will react.

Essentially your route is mapping to BoxedProductController, HomepageProducts action of Nop.Plugin.Product.BoxedProductView namespace. It is not like just because you format the route URL to look like Catalog/HomepageProducts, then your route will override the default render of Html.Action("Catalog", "HomepageProducts"). It will override if you visit ~/Catalog/HomepageProducts though.

What you can try instead is to use a route like:

            
routes.MapLocalizedRoute("Nop.Plugin.Product.BoxedProductView.HomepageProducts",
                 "TestC/TestA",
                 new { controller = "Catalog", action = "HomepageProducts" },
                 new[] { "Nop.Plugin.Product.BoxedProductView" }
            );


Like this, your route table have 2 entries matching Controller = Catalog and Action = HomepageProducts, but with different namespace. And because your route have a higher priority, it'll be used eventually. But what is making the difference is not the URL, it's the name space. Notice that I am using TestC/TestA as the URL, but it doesn't matter in case of Html.Action(). :)


Alright, now I understand! I renamed my plugin-Controller to CatalogController and it finally gets called. Thanks :)

Now the problem is that it cannot find the view in question, I keep getting:

The partial view 'Plugins/BoxedProductView/Views/HomepageProducts.cshtml' was not found or no view engine supports the searched locations. The following locations were searched:
~/Themes/DefaultClean/Views/Catalog/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.cshtml
~/Themes/DefaultClean/Views/Catalog/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.vbhtml
~/Themes/DefaultClean/Views/Shared/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.cshtml

Basically the engine is searching everywhere but the Plugins for the View.
10 years ago
chr2306 wrote:

Now the problem is that it cannot find the view in question, I keep getting:

The partial view 'Plugins/BoxedProductView/Views/HomepageProducts.cshtml' was not found or no view engine supports the searched locations. The following locations were searched:
~/Themes/DefaultClean/Views/Catalog/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.cshtml
~/Themes/DefaultClean/Views/Catalog/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.vbhtml
~/Themes/DefaultClean/Views/Shared/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.cshtml

Basically the engine is searching everywhere but the Plugins for the View.


And that was because my .cshtml-file was not set as Embedded Resource. Thanks so much for the help wooncherk :)
10 years ago
chr2306 wrote:

Now the problem is that it cannot find the view in question, I keep getting:

The partial view 'Plugins/BoxedProductView/Views/HomepageProducts.cshtml' was not found or no view engine supports the searched locations. The following locations were searched:
~/Themes/DefaultClean/Views/Catalog/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.cshtml
~/Themes/DefaultClean/Views/Catalog/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.vbhtml
~/Themes/DefaultClean/Views/Shared/Plugins/BoxedProductView/Views/HomepageProducts.cshtml.cshtml

Basically the engine is searching everywhere but the Plugins for the View.

And that was because my .cshtml-file was not set as Embedded Resource. Thanks so much for the help wooncherk :)


No problem friend. If you want to know more about displaying Views in plugin, try this article I've written: http://www.pronopcommerce.com/3-ways-to-display-views-in-your-nopcommerce-plugins-embedded-resource-theme-override-and-custom-view-engine
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.