The answer for this question is actually very simple. I don't know why the other users here recommended considerably lengthy solutions but in my opinion it can be carried out by adding the Route
attribute on the Details
API. When someone needs to display a list of products inside of a View
it is very unlikely and illogical if they only display the Product Id
and normally an IEnumerable<Product>
is passed to the Index
view in the situation asked in the question. Accordingly, the following can be done:
[Route("Products/Details/{Manufacturer=unnamed}/{Category=unnamed}/{ActualProduct=unnamed}/{Model=unnamed}/{URLTrail=unnamed}")]
public IActionResult Details(GUID id) {
return View(FindProduct(id));
}
Example output: www.MyOnlineShop.com/Products/Details/Microsoft/Surface/SurfacePro/xxxx-xxxx-....
Now it really does not matter if some of these URL parameters are missing but the user can have the option to render the URL as they like !.
Update:
Building the URL inside of the view is more SEO friendly as it creates reasonable links!
A better example, in case of stackoverflow, the URL for this question is
http://stackoverflow.com/questions/42472547/mvc-core-routing-for-displaying-detailed-information-in-the-url
In this example the actual question id is 42472547
and the trailing part is for SEO so that Google can search the question. In this example the following route applies:
[Route("Questions/{id}/{SEOHint}"]
and the {id}
part is used for looking up the question not the {SEOHint}
part.
Updated Solution
I have written the following attribute that can be customized for implementing this functionality.
public abstract class URLOptimizerAttributeBase : Attribute, IRouteTemplateProvider, IActionFilter
{
public String Template { get; private set; }
public int? Order { get; set; }
public string Name { get; set; }
public URLOptimizerAttributeBase(String Template) {
this.Template = Template;
}
public abstract Boolean Validate(RouteData routeData);
public abstract RedirectToRouteResult Redirect(RouteValueDictionary routeValueDictionary);
public abstract void OnActionExecuted(ActionExecutedContext context);
public virtual void OnActionExecuting(ActionExecutingContext context)
{
if (!Validate(context.RouteData))
{
context.Result = Redirect(context.RouteData.Values);
}
}
}
The following provides an example of the implementation of this attribute.
public class URLOptimization : URLOptimizerAttributeBase
{
private String SEOHint;
public URLOptimization(string Template) : base(Template)
{
}
public object DbContext { get; private set; }
public override void OnActionExecuted(ActionExecutedContext context)
{
}
public override bool Validate(RouteData routeData)
{
return routeData.Values["SEOHint"].Equals(SEOHint);
}
public override RedirectToRouteResult Redirect(RouteValueDictionary routeValueDictionary)
{
routeValueDictionary["SEOHind"] = SEOHint;
return new RedirectToRouteResult(routeValueDictionary);
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var id = context.RouteData.Values["Id"];
DbContext db = (DbContext)context.HttpContext.RequestServices.GetService(typeof(DbContext));
var product = db.Find<Products>(id);
if (product != null)
{
SEOHint = product.SEOURL;
((Controller)context.Controller).ViewBag.Data = product;
base.OnActionExecuting(context);
}
else {
context.Result = new RedirectResult("Error Page");
}
}
}
And the example of using this attribute would be as follows:
[URLOptimization("Products/Details/{Id}/{SEOHint=unnamed}")]
public IActionResult Details(){
return View(ViewBag.Data);
}