Reputation: 5459
Hi I have been trying to solve a problem for quite a while that it is related to routing and links with no luck.
I have an application that stores a number of books. Each book has links that displays it's details. In order to get the details I am using the ID for each book in my link. This is the code for the link:
@Html.ActionLink(book.Name, "Details", new { bookId = book.Id , book = book.BookUrl })
When the user clicks one of the links this is the controller that is being called and I am not allowed to modify it:
public ActionResult Details (int bookId){..}
And this is how my link will be displayed:
http://localhost:51208/Home/Details/C-sharp-5.0-in-a-Nutshell-The-Definitive-Reference/BookId-2
What I would like is someway to stop displaying the controler action and BookId.So my link would look something like this:
http://localhost:51208/C-sharp-5.0-in-a-Nutshell-The-Definitive-Reference
From what I can tell so far there is no way to do this using the standard functionality of routing so I figured I have to extend it's functionality somehow.
I have been reading about this in PRO ASP.NET MVC 4 and from what I have read I have to create a class and inherit from RouteBase
.
This will result in having to implement two methods one is RouteData GetRouteData(HttpContextBase httpContext)
and the other VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
So far I only understand that GetVirtualPath
is responsible for generating the links so I figure this is where I would have to create the implementation to get the functionality I desire.
If this is the case can someone give me an idea on how I should implement this method?
If this is not the route to take then how can I achieve what I want?
Upvotes: 0
Views: 110
Reputation: 4770
You could do this by implementing your own version of RouteBase. You would need to implement both GetRouteData
and GetVirtualPath
.
Your implementation of GetRouteData
would need to check your database of books to see if the URL contains the name of a book in your database, and then return the correct route data e.g.
public class BookRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var url = httpContext.Request.AppRelativeCurrentExecutionFilePath;
url = url.Replace(@"~/", "");
var book = getBookWithTheNameThatMatchesTheUrl(url);
if (book != null)
{
var rd = new RouteData(this, new MvcRouteHandler());
rd.Values.Add("controller", "Home");
rd.Values.Add("action", "Details");
rd.Values.Add("bookId", book.Id);
return rd;
}
return null;
}
and your implementation of GetVirtualPath
would need to do the opposite - find the name of the book with the given ID, and return the appropriate URL.
e.g.
public override VirtualPathData GetVirtualPath(RequestContext requestContext,
RouteValueDictionary values)
{
if ((string)values["action"] == "Details" &&
(string)values["controller"] == "Home")
{
var bookId = (int)values["bookId"];
var bookName = LookUpTheBookNameForTheBookWithThisId(bookId);
return new VirtualPathData(this, bookName);
}
return null;
}
Lastly, add routes.Add(new BookRoute());
where you are registering the routes to enable this new route to be checked.
Given that the method getBookWithTheNameThatMatchesTheUrl()
would hit the database for every page, you'd want to be caching the look-ups.
Upvotes: 1
Reputation: 9458
You can put an extra route in your RouteConfig.cs
as below:
routes.MapRoute(
"Book Details", // Route name
"{ bookName }", // URL with parameters
new { controller = "BookController",
action = "Details",
name = UrlParameter.Optional
} // Parameter defaults
);
Change your Details
method accordingly, but its parameter must be bookName
.
Note :
Put this route at the bottom of all routes you have defined for avoiding ambiguity.
Upvotes: 2