From the SEO standpoint it is nice to see urls in format which explains what is located on a page
Let's have a look on such situation (it is just example)
We need to display page about some product and decided to have such url template for that page:
/product/{ProductId}/{ProductCategory}/{ProductUrlName}
.
And create for this purpose such model
public class ProductUrlInfo{
public int ProductId{get;set;}
public string ProductCountry{get;set;}
public string ProductUrlName{get;set;}
}
I want to create controller method where I pass ProductUrlInfo object but not all required fields. Classic controller method for url template shown above is following
public ActionResult Index(int ProductId, string ProductCategory, string ProductUrlName){
return View();
}
and we need to call it like that
Html.ActionLink<UserController>(x=>Index(user.ProductId, user.ProductCategory, user.ProductUrlName), "See user page")
I want to create such controller method
public ActionResult Index(ProductUrlInfo productInfo){
return View();
}
and call it like that: Html.ActionLink<ProductController>(x=>Index(product), "See product page")
Actually I works when we add one more route and point it to the same controller method, so routing will be:
/product/{productInfo}
/product/{ProductId}/{ProductCategory}/{ProductUrlName}
In this situation routing engine gets string method of our model (need to override it) and it works ALMOST always. But sometimes it fails and show url like
/page/?productInfo=/Cars/Porsche911
So my workaround does not always work properly. Does anybody know how to work with urls in such way?
Edit Maybe it was unclear, sorry... ProductUrlInfo is NOT a view model. That is an object which created just to be shown in the url ONLY. Example of all object for such product
public class ProductList: List<Product>{}
public class Product{
public int ProductId{get;set;}
public string ProductCountry{get;set;}
public string ProductName{get;set;}
public string Height{get;set;}
public float Price{get;set;}
//data which must be rendered in the url string
public ProductUrlInfo Key(){
return new ProductUrlInfo(){
ProductId = this.ProductId
,ProductCountry = MyConverter.EncodeForUrl(this.ProductCountry)
,ProductUrlName= MyConverter.EncodeForUrl(this.ProductName)
}
}
}
Somewhere on a View control:
foreach( var pr in Model)
Html.RenderActionLink<ProductController>(x=>Index(pr.Key), "See product page")
Controller methods should be like this:
//accept our complex object as a key
public ActionResult Index(ProductUrlInfo key){
//retrive data from database or any other stuff
Product pr = Repository.GetProductByKey(key);
return View(pr);
}
//accept our complex object as a key
public ActionResult Edit(ProductUrlInfo key){
//retrive data from database or any other stuff
Product pr = Repository.GetProductByKey(key);
return View(pr);
}
[HttpPost]
public ActionResult Edit(Product product){
//do update here
}
Let me explain how it passed to Index(ProductUrlInfo key)
controller method.
I think that I'm using some side effect. But at first if you have several items you want to pass to controller and actually only single item is object primary key (i.e. object ID) and other elements just explain user which page he has opened (some additional information) (i.e. object category and name). So in a future you might want to change this information (add new fields/ remove old etc.) But if you have links to that page from all over the project then it may be quite painful to completely replace links to new format. So why not pass to controller some model which then be rendered into url?
I investigated that if you can do following steps to pass custom object to controller
1. create class which contains all required fields to be shown in url (ProductUrlInfo class here)
register route with your actually passed object (in my particular case it is "/{key}" )
here some magic. Register AFTER that your desired url format routes ("/{id}/{Category}/{ProductUrlName}" and for example "/{id}/{ProductUrlName}") You might want to render only name if it is unknown its category for some reason
override
ToString
method inProductUrlInfo
class and it must render your desired url format. i.e.public override string ToString()
{
return (!String.IsNullOrEmpty(Category))
? string.Format( "{0}/{1}/{2}", Id, Category, ProductUrlName )
: string.Format( "{0}/{1}", Id, ProductUrlName ); }As I do understand that effect: when routing engine gets passed object it looks for appropriate route and found {key} string. Then it found that passed object is a complex type and calls its ToString method. But its not blindly set result instead of {key} parameter but also compares it against routes. Now if you pass ProductUrlInfo into ActionLink as a parameter you will be tranfered to Index(ProductUrlInfo key) controller method. Of course it looks for me as a hack so I'd like to know is there anybody who passes objects into GET controller method but in some other (better) way?