Reputation: 5637
I'm building a site using NopCommerce 2.30. Out of the box, the product URLs look like this:
/p/16/build-your-own-computer/
I've made some changes to get the URLs looking like this:
/build-your-own-computer/
I've made a change to a Route
to stop it from using the Id
parameter. However, all the links across the site are still passing the Id parameter through the Url.RouteUrl
method:
@Url.RouteUrl("Product", new { productId = Model.Id, SeName = Model.SeName })
This results in the following URL:
/build-your-own-computer/?productId=16
I could just go through and remove the productId
parameter from the method call. However, some plugins also add links containing the productId
, which I'm unable to change.
Is there a way I can override the Url.RouteUrl
method to do a check for requests to the product Route
and then remove the productId
?
Upvotes: 0
Views: 2747
Reputation: 5637
I've managed to resolve my issue using a slightly modified solution of this: http://erraticdev.blogspot.com/2011/03/removing-route-values-from-linksurls-in.html
Robert's solution didn't include namespaces so I made a few adjustments below:
public static class CustomRouteExtensions
{
public static Route MapRoute(this RouteCollection routes, string name, string url, string excludeRouteValueNames, object defaults)
{
return MapRoute(routes, name, url, excludeRouteValueNames, defaults, null, null);
}
public static Route MapRoute(this RouteCollection routes, string name, string url, string excludeRouteValueNames, object defaults, string[] namespaces)
{
return MapRoute(routes, name, url, excludeRouteValueNames, defaults, null, namespaces);
}
public static Route MapRoute(this RouteCollection routes, string name, string url, string excludeRouteValueNames, object defaults, object constraints)
{
return MapRoute(routes, name, url, excludeRouteValueNames, defaults, constraints, null);
}
public static Route MapRoute(this RouteCollection routes, string name, string url, string excludeRouteValueNames, object defaults, object constraints, string[] namespaces)
{
if (routes == null)
throw new ArgumentNullException("routes");
if (url == null)
throw new ArgumentNullException("url");
Route item = new CustomRoute(url, new MvcRouteHandler(), excludeRouteValueNames)
{
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
if ((namespaces != null) && (namespaces.Length > 0))
item.DataTokens["Namespaces"] = namespaces;
routes.Add(name, item);
return item;
}
}
My custom Route
then looks like this:
public class CustomRoute : Route
{
#region Properties
public ReadOnlyCollection<string> ExcludedRouteValuesNames { get; private set; }
#endregion
#region Constructor
public CustomRoute(string url, IRouteHandler routeHandler, string commaSeparatedRouteValueNames)
: this(url, routeHandler, (commaSeparatedRouteValueNames ?? string.Empty).Split(','))
{
}
public CustomRoute(string url, IRouteHandler routeHandler, params string[] excludeRouteValuesNames)
: base(url, routeHandler)
{
ExcludedRouteValuesNames = new ReadOnlyCollection<string>(excludeRouteValuesNames.Select(val => val.Trim()).ToList());
}
#endregion
#region Route overrides
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
if (requestContext == null)
throw new ArgumentNullException("requestContext");
// create new route data and include only non-excluded values
var excludedRouteData = new RouteData(this, this.RouteHandler);
// add route values
requestContext.RouteData.Values
.Where(pair => !this.ExcludedRouteValuesNames.Contains(pair.Key, StringComparer.OrdinalIgnoreCase))
.ToList()
.ForEach(pair => excludedRouteData.Values.Add(pair.Key, pair.Value));
// add data tokens
requestContext.RouteData.DataTokens
.ToList()
.ForEach(pair => excludedRouteData.DataTokens.Add(pair.Key, pair.Value));
// intermediary request context
var currentContext = new RequestContext(new HttpContextWrapper(HttpContext.Current), excludedRouteData);
// create new URL route values and include only none-excluded values
var excludedRouteValues = new RouteValueDictionary(
values
.Where(v => !this.ExcludedRouteValuesNames.Contains(v.Key, StringComparer.OrdinalIgnoreCase))
.ToDictionary(pair => pair.Key, pair => pair.Value)
);
var result = base.GetVirtualPath(currentContext, excludedRouteValues);
return result;
}
#endregion
}
My new Route.MapRoute
looks like this:
routes.MapRoute("Product",
"{SeName}",
"productIdd",
new { controller = "Catalog", action = "UrlType" },
new[] { "Nop.Plugin.FreshEgg.Seo.Controllers" });
Upvotes: 1
Reputation: 33071
There is no need to override RouteUrl. It takes in a collection of key/value pairs. Just modify the route so it no longer expects the id and remove the id from the @Url.RouteUrl call. You will need to create a route that accepts URLs like the following:
/p/{productName}
You can do that in your Global.asax:
routes.MapRoute(
"Products",
"/p/{productName}",
new { controller = "Products", action = "Details" }
);
Now you would generate a route like this instead:
@Url.RouteUrl("Products", new { productName = Model.SeName })
Now in your controller you have to additional work to find a product from a name instead of an id. It will probably look something like this now instead:
public ActionResult Details(string productName)
{
var product = ProductRepository.FindByName(productName);
return View(product);
}
Upvotes: 0