MVC custom routing function - asp.net-mvc

This is a more specific version of another of my questions: Restful MVC Web Api Inheritance, I hope an answer to this will help me answer that.
Im using ASP.NET web api,
I want to be able to route something like this: [{object}/{id}]/{controller}/{id}.
so i want an array of objects with optional /{id} ending with the 'api endpoint'.
I want to be able to route these:
/houses
/houses/3
suburbs/3/houses
council/5/suburbs/houses
city/8/council/suburbs/houses
ETC
TO
get(List<restRoute>parents, int id){
...
}
restRoute would be an object with a string for the object and an optional int (or guid etc) for the id
Does anyone know where i can start?
I don't want to route every single one individually.

I had also such problems with routing from the box in ASP.NET MVC. Its good way to be used as common routing, but is not so flexible for custom routs.
In WCF Web Api (ASP.NET web api in CTP version) was used attribute based routing.
I think its more flexible, but as negative point - each method should have routing attribute.
Take a look at this blog post:
http://www.strathweb.com/2012/05/attribute-based-routing-in-asp-net-web-api/
It describes how to implement attribute based routing using ASP.NET Web Api. Because such approach is more flexible for routes you can map to methods, it can be helpful for you.

You could use the {*anything} Variable Segmented URL pattern in your route and handle the splitting up and figuring out of what part of the url corresponds to what bit of data in your method:
Global.asax:
routes.MapRoute(
"Special", // name
"{*allthethings}", // parameters
new { controller = "Special", action = "Sauce" } // defaults
);
SpecialController:
public ActionResult Sauce()
{
string data = RouteData.Values["allthethings"].ToString();
string[] items = data.Split('/');
foreach (string item in items)
{
// do whatever you need to figure out which is what!
}
return View();
}
If you wanted to be a bit cleverer about it you could create your own custom RouteHandler to do the splitting. Something like David Ebb's PK routehandler would probably do the trick, with some customisation to fit your requirements in the processing of the route. You could use this to split up the "allthethings" parameter and turn it into your List<RestRoute> format before passing the request on to the Controller

Related

Best way to add filter to URL in .NET MVC

I'll try to make this as concise as possible.
Webpage contains a table that allows for filtering and sorting
Changes to filtering and sorting should be reflected in the URL so the user can bookmark or share filtered views.
The question is: What is an effective convention of allowing all of the sort and filter syntax to be part of the URL and easily interpret/use it on the server without having to write a bunch of custom code that interprets it?
I've been doing some research and I came across the OData URI conventions and I like the way they do things.
http://www.odata.org/developers/protocols/uri-conventions
More research shows the the MVC 4 Web API allows for use of that convention by returning an IQueryable. This looks fantastic except for one part... I'm not implementing a RESTful API at this point and that's all it seems to work with. So how can I use something like OData and still return a View or PartialView? Is there something that will parse the OData URI convention into a C# object?
If anyone has any insights into this problem or suggestions, I'm all ears.
As for the url convention part of your question, I think you have answered your own question with OData. As for getting this data into a C# object I would use the following approach:
Use an action filter to interperet the url parameters and parse them into a c# object.
In your action filter add the url parameters to the route data and the c# object will be available in your action.
ASP.NET MVC Pass object from Custom Action Filter to Action
Take a look at the Telerik MVC grid, they use a GridAction action filter that does pretty much what you are asking.
I would look at custom model binding. A good overview can be found here: http://weblogs.asp.net/nmarun/archive/2010/02/25/asp-net-mvc-model-binding.aspx
It's typically used for POST requests with forms but there's no reason why you can't use it for GET requests too.
Basically, your approach should be to:
Create a new Model class with your filter/sorting parameters as properties:
public class TableParameters {
public string TableFilter { get; set; }
}
In your Controller's Action, add the model as a parameter
public ActionResult TableAction(TableParameters parameters) { /* Action logic */ }
Set your parameters in the URL by saying:
/Controller/TableAction?TableFilter=[your-filter-string]
The parameters object in your action will have the property populated with the value from the query string.

Creating a single controller to return both data and views

I've been playing around with ASP.NET MVC 4 beta and I noticed two controllers: one(web api controller) to return data and the other(default controller) to return views.
I'm aware that even HTML is a type of representation for a resource like JSON or XML and I'm not personally so happy having a separate controller to return only HTML representation.
What I'm trying to achieve is having a single controller that will return both views and data. But I'm confused is this a best option and if it is how can I achieve this without much code.
Can I extend the ApiController such that it will return both views as well as data or can I create a generic controller that by passes the requests to particular controller type based upon the "accept-type".
I'm looking for your valuable suggestions.
Write a formatter rendering your HTML.
The official Contact Manager example describes how to use formatters to render in any of the following html/json/png/vcf within a single controller.
You'll have to add the valid routes, if you want to explicitly request within the url.
config.Routes.MapHttpRoute(
"Default", // Route name
"{controller}/{id}/{ext}", // URL with parameters
new { id = RouteParameter.Optional, ext = RouteParameter.Optional } // Parameter defaults
);
But if you are actually setting the "accept-type" header, that should pretty much work out of the box

Combining values in ASP.Net Routes for MVC

Can anyone suggest a way to accomplish the following in ASP.Net Routing (for MVC 3)?
I want to have URLs where the value which determines the controller is actually part of the id for the page:
/{id}-{controller}/{action}/{further-values}
But I need the id value to include the value used for the controller as well, so in the above if we have the following URL:
/chelsea-football-team/view/2010-2011
I want the {id} value to, ideally, be "chelsea-football-team", the controller to be "football-team", the action to be "view" and the additional value to be "2010-2011".
I have no issues having several routes with the controller value hard coded into the route definition, but I need to be able to have several controller values.
I know that I can simply combine the values in the controller, but that adds a lot of additional, repeated code - so is this accomplishable in any other way?
Why do I want to do this? Because I need to have the team name in full, but part of the team name will always match the controller name so why not combine them in the route?
I want the {id} value to, ideally, be "chelsea-football-team", the controller to be "football-team", the action to be "view" and the additional value to be "2010-2011".
...MapRoute(null, "{id}/{action}/{*furtherValues}",
new {
controller = "FootballTeam",
});
Update after comment 1
You can't combine route parameters in a URL such that a single parameter represents both a variable (id) and a controller, using the standard routing implementation. If you want the id to be "chelsea-football-team", that has to be a self-contained route parameter. You can't combine it in a way that MVC extracts the controller name from the id.
To meet this requirement, you may have to create a custom RouteBase implementation:
public class MyCustomRoutingPattern : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
// do your coding and return an instance of RouteData
}
}
You would then use it like so:
routes.Add(new MyCustomRoutingPattern(
"{id}/{action}/{*furtherValues}"));
Your RouteBase implementation can then extract the controller from the id parameter. The Pro ASP.NET MVC3 Framework book by Steve Sanderson and Adam Freeman has a section on how to override the RouteBase class.

URLs the asp.net mvc way

In the conventional website a url displayed as:
http://www.mySite.com/Topics
would typically mean a page sits in a subfolder below root named 'Topics' and have a page named default.htm (or similar).
I'm trying to get my head in gear with the MVC way of doing things and understand just enough of routing to know i should be thinking of URLs differently.
So if i have a db-driven page that i'd typically script in a physical page located at /Topics/index.aspx - how does this look in an MVC app?
mny thx
--steve...
It sounds like you are used to breaking down your website in terms of resources(topics, users etc) to structure your site. This is good, because now you can more or less think in terms of controllers rather than folders.
Let's say you have a structure like this in WebForms ASP.NET.
-Topics
-index.aspx
-newtopic.aspx
-topicdetails.aspx
-Users
-index.aspx
-newuser.aspx
-userdetails.aspx
The structure in an MVC app will be pretty much the same from a users point of view, but instead of mapping a url to a folder, you map a url to a controller. Instead of the folder(resource) having files inside it, it has actions.
-TopicController
-index
-new
-details
-UserController
-index
-new
-details
Each one of these Actions will then decide what view (be this html, or json/xml) needs to be returned to the browser.
Actions can act differently depending on what HTTP verb they're repsonding to. For example;
public class UserController : Controller
{
public ActionResult Create()
{
return View(new User());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(User user)
{
// code to validate /save user
if (notValid)
return new View(user);
else
return new View("UserCreatedConfirmation");
}
}
This is sort of a boiled down version of RESTful URLs, which I recommend you take a look at. They can help simplify the design of your application.
It looks just like you want it to be.
Routing enables URL to be quite virtual. In asp.net mvc it will end at specified controller action method which will decide what to do further (i.e. - it can return specified view wherever it's physical location is, it can return plain text, it can return something serialized in JSON/XML).
Here are some external links:
URL routing introduction by ScottGu
ASP.NET MVC tutorials by Stephan Walther
You would have an default view that is associated with an action on the Topics controller.
For example, a list page (list.aspx) with the other views that is tied to the list action of the Topics controller.
That is assuming the default routing engine rules, which you can change.
Read more here:
http://weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx
IMHO this is what you need for your routes.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Topics", action = "Index", id = "" } // Parameter defaults
);
You would need a TopicsController that you build the View (topics) on.

ASP.NET MVC routing

Up until now I've been able to get away with using the default routing that came with ASP.NET MVC. Unfortunately, now that I'm branching out into more complex routes, I'm struggling to wrap my head around how to get this to work.
A simple example I'm trying to get is to have the path /User/{UserID}/Items to map to the User controller's Items function. Can anyone tell me what I'm doing wrong with my routing here?
routes.MapRoute("UserItems", "User/{UserID}/Items",
new {controller = "User", action = "Items"});
And on my aspx page
Html.ActionLink("Items", "UserItems", new { UserID = 1 })
Going by the MVC Preview 4 code I have in front of me the overload for Html.ActionLink() you are using is this one:
public string ActionLink(string linkText, string actionName, object values);
Note how the second parameter is the actionName not the routeName.
As such, try:
Html.ActionLink("Items", "Items", new { UserID = 1 })
Alternatively, try:
Items
Can you post more information? What URL is the aspx page generating in the link? It could be because of the order of your routes definition. I think you need your route to be declared before the default route.
Firstly start with looking at what URL it generates and checking it with Phil Haack's route debug library. It will clear lots of things up.
If you're having a bunch of routes you might want to consider naming your routes and using named routing. It will make your intent more clear when you re-visit your code and it can potentially improve parsing speed.
Furthermore (and this is purely a personal opinion) I like to generate my links somewhere at the start of the page in strings and then put those strings in my HTML. It's a tiny overhead but makes the code much more readable in my opinion. Furthermore if you have or repeated links, you have to generate them only once.
I prefer to put
<% string action = Url.RouteUrl("NamedRoute", new
{ controller="User",
action="Items",
UserID=1});%>
and later on write
link
Html.ActionLink("Items", "User", new { UserID = 1 })

Resources