Areas And Routes - asp.net-mvc

I'm using areas everywhere and I'm wanting something like the following:
http://localhost/MyArea/MySection/MySubSection/Delete/20
Usually I access things by doing the following:
http://localhost/MyArea/MySection/MySubSection/20
But if I want to delete then I have to say
http://localhost/MyArea/MySection/DeleteEntryFromMySubSection/20
With routes, how do you do this? (the routes aren't realistic by the way, they're much more concise than this in my system)
EDIT: This is specifically related to the use of Areas, an ASP.NET MVC 2 Preview 2 feature.

It would depend on how your routes & controllers are currently structured.
Here's an example route you might want to use.
If you want to be able to call the following route to delete:
http://localhost/MyArea/MySection/MySubSection/Delete/20
And let's assume you have a controller called "MyAreaController", with an action of "Delete", and for the sake of simplicity let's assume section and subsection are just strings e.g.:
public class MyAreaController : Controller
{
public ActionResult Delete(string section, string subsection, long id)
{
Then you could create a route in the following way (in your Global.asax.cs, or wherever you define your routes):
var defaultParameters = new {controller = "Home", action = "Index", id = ""};
routes.MapRoute("DeleteEntryFromMySubSection", // Route name - but you may want to change this if it's used for edit etc.
"{controller}/{section}/{subsection}/{action}/{id}", // URL with parameters
defaultParameters // Parameter defaults
);
Note: I'd normally define enums for all the possible parameter values. Then the params can be of the appropriate enum type, and you can still use strings in your path. E.g. You could have a "Section" enum that has a "MySection" value.

Related

ASP.NET MVC Dynamic Action with Hyphens

I am working on an ASP.NET MVC project. I need to be able to map a route such as this:
http://www.mysite.com/Products/Tennis-Shoes
Where the "Action" part of the URL (Tennis-Shoes") could be one of a list of possibilities. I do not want to have to create a separate Action method in my controller for each. I want to map them all to one Action method and I will handle the View that is displayed from there.
I have this working fine by adding a route mapping. However, there are some "Actions" that will need to have a hyphen in them. ASP.NET MVC routing is trying to parse that hyphen before I can send it to my action. I have tried to create my own custom Route Handler, but it's never even called. Once I had a hyphen, all routes are ignored, even my custom one.
Any suggestions? Details about the hyphen situation? Thanks you.
Looking at the URL and reading your description, Tennis-Shoes in your example doesn't sound like it should be an action, but a Route parameter. Let's say we have the following controller
public class ProductsController : Controller
{
public ActionResult Details(string product)
{
// do something interesting based on product...
return View(product);
}
}
The Details action is going to handle any URLs along the lines of
http://www.mysite.com/Products/{product}
using the following route
routes.MapRoute(
null,
"Products/{product}",
new
{
controller = "Products",
action = "Details"
});
You might decide to use a different View based on the product string, but this is just a basic example.

What do the curly braces mean in a route in MVC?

I'm setting up my routes with an MVC project but im a little confused about the curly braces...
If I have...
routes.MapRoute( "Music", "Music/{name}", new { } );
What is the purpose of the curly braces around name, does this get passed to something? Or does this map to something if I pass a default object in?
They are parameter names that are used in routing requests. For example the default route defines three of them:
{controller}/{action}/{id}
controller and action parameters are for finding your controller action. id parameter can be used as an input in those actions.
When you define a custom route you have to provide controller and action parameters. If they are not defined in your URL, you should provide default values so MVC knows what action to run when a request matches that route.
routes.MapRoute("Music",
"Music/{name}",
new { controller="Music", action="SomeAction" });
Other parameters like id or name like you defined can be used to provide input to actions. In your example, name parameter is passed to matching action like this:
public ActionResult SomeAction(string name)
{
//do something
}
The curlybraces indicate a kind of named wildcard.
The "Music/Index" route will only match the URL Music/Index and nothing else
The "Music/{Name}" route will match any URLs starting with Music, and having anything after the slash. It will match both the URLs Music/metallica and Music/madonna.
With the curly brace, you'll be able to pick up "metallica" or "madonna" from the above URLS as routevalues.
As a final example: With ASP.NET MVC, there's always a standard route. {controller}/{action}/{id}. This route will catch URLs like Music/genre/rock or Product/edit/5.
The resulting routevalues for these two will be:
controller=music, action=genre and id=rock for the first one
controller=product, action=edit and id=5 for the last one.
I'll try to provide a less contrived example.
Routes in ASP.NET MVC are placed into a dictionary, and when there's an incoming request, the MVC pipeline looks at the request and tries to determine what Controller and Action to route it to.
So let's say I have the following controllers: Home, Forum, and Article
And while we're at it, let's say I have the following actions: View, Edit, Create on both the Forum and Article controllers.
Those braces allow me to create one route for both:
routes.MapRoute("Viewing",
{controller}/{action}/{id},
new {controller = "Article", action="" }, //The article controller has precedence
new { controller = "Article|Forum" } //contrived for this example
);
Those braces mean that whatever controller they put in (as long as it's Article or Forum based on the Constraints), the same route works. This keeps me from having to have a route for each and every action in the Forum and Article controller.
I could have just as easily made two routes:
routes.MapRoute("Articles",
article/{action}/{id},
new {controller = "Article" } //The article controller has precedence
);
routes.MapRoute("Forums",
forum/{action}/{id},
new { controller = "forum" }
);
But there's duplication there that doesn't need to be there.
Routes are also pretty tricky things, in that order matters. The top route will be evaluated before the bottom route. If it matches the top route's structure, it will go to that action, even if that's not the right action.
Phil Haack has a Route Debugger that helps with this. And I've also taken his source code and modified it so that you can make it a control and put it on all your pages as a partial (and hopefully you will also put code on there that would only allow internal folks to see it).

ASP.NET MVC routing: with querystring to one action, without to another

I need the following routes:
example.com/products
goes to a product categories page (e.g. cars, trucks, buses, bikes)
controller=Products, action=Categories()
example.com/products?a=1&b=2
goes to an index of all products in a particular category (e.g. Ford, Honda, Chevy)
controller=Products, action=Index(string a, string b)
The routes only differ on the querystring, and it seems that MVC ignores anything after the "?". So of course only one rule will ever get hit--the first one.
How do I differentiate between the two?
Edit: stated differently, I want two routes. Is it possible to use the querystring in the route or does MVC truly ignore it? Is there any way to hack it, or use a custom routing scheme of some kind, much like I can do custom binding and custom validation?
Introduce Parameters. ASP.NET MVC allows you to create 'pretty' URLs, and that's exactly what you should do here:
First, the route mappings:
routes.MapRoute(
"SpecificProducts",
"products/{a}/{b}",
new { controller = "products", action = "Categories" }
);
routes.MapRoute(
"ProductsIndex",
"products".
new { controller = "products", action = "Index" }
);
Then, the controller actions
public ActionResult Index()
{
}
public ActionResult Categories(string a, string b) //parameters must match route values
{
}
This will allow you to use a search-friendly URL and you don't have to worry about query string parameters.

How to route legacy type urls in ASP.NET MVC

Due to factors outside my control, I need to handle urls like this:
http://www.bob.com/dosomething.asp?val=42
I would like to route them to a specific controller/action with the val already parsed and bound (i.e. an argument to the action).
Ideally my action would look like this:
ActionResult BackwardCompatibleAction(int val)
I found this question: ASP.Net MVC routing legacy URLs passing querystring Ids to controller actions but the redirects are not acceptable.
I have tried routes that parse the query string portion but any route with a question mark is invalid.
I have been able to route the request with this:
routes.MapRoute(
"dosomething.asp Backward compatibility",
"{dosomething}.asp",
new { controller = "MyController", action = "BackwardCompatibleAction"}
);
However, from there the only way to get to the value of val=? is via Request.QueryString. While I could parse the query string inside the controller it would make testing the action more difficult and I would prefer not to have that dependency.
I feel like there is something I can do with the routing, but I don't know what it is. Any help would be very appreciated.
The parameter val within your BackwardCompatibleAction method should be automatically populated with the query string value. Routes are not meant to deal with query strings. The solution you listed in your question looks right to me. Have you tried it to see what happens?
This would also work for your route. Since you are specifying both the controller and the action, you don't need the curly brace parameter.
routes.MapRoute(
"dosomething.asp Backward compatibility",
"dosomething.asp",
new { controller = "MyController", action = "BackwardCompatibleAction"}
);
If you need to parametrize the action name, then something like this should work:
routes.MapRoute(
"dosomething.asp Backward compatibility",
"{action}.asp",
new { controller = "MyController" }
);
That would give you a more generic route that could match multiple different .asp page urls into Action methods.
http://www.bob.com/dosomething.asp?val=42
would route to MyController.dosomething(int val)
and http://www.bob.com/dosomethingelse.asp?val=42
would route to MyController.dosomethingelse(int val)

Refactoring my route to be more dynamic

Currently my URL structure is like this:
www.example.com/honda/
www.example.com/honda/add
www.example.com/honda/29343
I have a controller named HondaController.
Now I want to refactor this so I can support more car manufacturers.
The database has a table that stores all the manufacturers that I want to support.
How can I keep my URL like above, but now support:
www.example.com/ford
www.example.com/toyota/add
etc.
I can easily rename the HondaController to CarController, and just pass in the string 'honda' or 'toyota' and my controller will work (it is hard coded to 'honda' right now).
Is this possible? I'm not sure how how to make a route dynamic based on what I have in the database.
Any part of your route can be dynamic just be making it into a route parameter. So instead of "/honda/{action}", do:
/{manufacturer}/{action}
This will give you a parameter called "manufacturer" that was passed to your action method. So your action method signature could now be:
public ActionResult add(string manufacturer) { }
It would be up to you to verify that the manufacturer parameter correctly matched the list of manufacturers in the database - it would probably be best to cache this list for a quicker lookup.
Updated: What I mean by "you have to take out the default parameters" for the default route is this. If you have:
route.MapRoute("Default", "/{controller}/{action}/{id}",
new { id = 1 } // <-- this is the parameter default
);
then this route will match any url with two segments, as well as any url with three segments. So "/product/add/1" will be handled by this route, but so will "/product/add".
If you take out the "new { id = 1 }" part, it will only handle URL's that look like "/product/add/1".
i have made something like this for granite as i wanted to have a material controller but have a url like so:
black/granite/worktops
black/quartz/worktops
etc
i did this route:
routes.MapRoute("Quote", "quote/{color}/{surface}/{type}",
new {controller = "Quote", action = "surface"});
swap quote for car so u can have:
car/honda/accord
your route can then be
routes.MapRoute("cars", "car/{make}/{model}",
new {controller = "Cars", action = "Index"});
your actionResults can then look like this:
public ActionResult Index(string make, string model)
{
//logic here to get where make and model
return View();
}
that i think covers it
What I recommend is instead using:
domain/m/<manufacturer>/<action>
Where 'm' is the manufacturer controller. This will allow you to use the same controller for all of your extensions and save you a lot of headache in the future, especially when adding new features. Using a one-letter controller is often times desirable when you want to retain your first variable ( in this case) as the first point of interest.

Resources