Strategy for Hierarchical MVC Routing - asp.net-mvc

I am sure this has been answered somewhere else but I cannot seem to find a definitive posting anywhere.
Most of the postings regarding hierarchical routing talks about when you want an unlimited number of tokens in the Url. My question deals more with when it does not make sense for a given Entity to exists without being associated in the context of another Entity.
For example, I have a Contract entity along with the expected Contract controller. It has the standard Actions like Index, Edit, Create, and so on. My Urls look like
/Contracts/ ' list all contracts
/Contracts/Create/ ' display form to create new contract
/Contracts/Edit/87Y5r3/ ' display form to edit contract 87Y5r3
Now imagine that I Order entity that must be associated with a given Contract. Using the (almost) default routing I would have Urls of
/Orders/ ' display all orders across all contracts
/Orders/Index/87Y5r3 ' display all orders for contract 87Y5r3
/Orders/Create/87Y5r3 ' display form to create new order for contract 87Y5r3
/Orders/Edit/87Y5r3/45 ' display form to edit order 45 under contract 87Y5r3
I can of course leave the almost default routing while tweaking it to support the additional parameters like contract number and order number.
Or I can change my routing to show that Orders comes under Contracts in the hierarchy. As I see it, I have several paths to pursue:
1) Have a single controller that handles both Contracts and Orders along with numerous custom routes for mapping actions to methods. This gives me Urls along the lines of
/Contracts/ ' maps to Index action in the Contract controller
/Contracts/Create/ ' maps to Create action in the Contract controller
/Contracts/Orders/ ' maps to IndexOrders action in the Contract controller
/Contracts/Orders/Index/87Y5r3 ' maps to IndexOrders action in the Contract controller
/Contracts/Orders/Edit/87Y5r3/45 ' maps to EditOrders action in the Contract controller
While I cannot imagine any good argument for having just a single controller, I am guessing that this good also be split into a Contracts controller and an Orders controller with an appropriate route(s).
The main point to take in is that the Contract Number is coming towards the end of the Url.
2) Another option is the separate controllers but with the following Urls. This seems a bit more natural (logical) to me.
/Contracts/ ' maps to Index action in the Contract controller
/Contracts/Create/ ' maps to Create action in the Contract controller
/Contracts/?????/87Y5r3/Orders/Index/ ' maps to Index action in the Order controller
/Contracts/?????/87Y5r3/Orders/Edit/45 ' maps to Edit action in the Order controller
/Contracts/?????/All/Orders/ ' maps to Index action in the Order controller
In this case the contract number comes after the Contract token in the Url with the order number coming towards the end. I have identified a few issues/concerns
How to handle Order data that goes across all Contracts. As you can see handled that with a special "All" token.
What action do I use for the Contracts portion of the Url. By default, routing in Mvc is /{controller}/{action}/{id}.
3) The third option I have seen posted (but do not understand enough to evaluate pros and cons) is to use RESTful API. I believe this would (could ??) resolve my second concern about what action to use for the Contract when working with Orders. The basic idea is that the action is replaced with an HTTP verb like DELETE or PUT which would only need to apply to the entity at the end of the Url.
In this case I would end up with something like
GET /Contracts/ ' maps to Index action in the Contract controller
POST /Contracts/Create/ ' maps to Create action in the Contract controller
GET /Contracts/87Y5r3/Orders/ ' maps to Index action in the Order controller
PUT /Contracts/87Y5r3/Orders/45 ' maps to Edit action in the Order controller
GET /Contracts/All/Orders/ ' maps to Index action in the Order controller
While RESTful may be the way to go I definitely do not know enough about it and my initial reaction is that it adds complexity and limitations (how many verbs are there???) that may limit its usefulness.
Based on my quick reading, going with a RESTful approach (including ASP.NET Web API as suggested by #Robotsushi below) does not really answer my question. RESTful seems to be something to be considered if my pages were requesting data via AJAX and JSON. In that sense (requesting data only) it provides an approach to Urls. However, my question is focused more on the standard MVC model where the action passes a Model to a View. At the end of the day I still need to present web pages to my users...
Did I sum this up clearly? Any other strategies that I am missing? This has to be a fairly common scenario so I am surprised I have not found a ton of articles.
I simplified the examples a bit but in my case I actually need to take this to a third level --- Contracts / Orders / Projects.
Thanks

This is purely my opinion.
I would recommend that you use a web service. If you can't you can still use HTTP POST. It will be easier to send a complex data structure without cluttering the URL with alot of unstructured key/value pairs.
If you used this strategy you would be able to send XML or JSON as your data structure, and get the complex entity representation you probably need.
If you are unfamiliar with HTTP based web services, then check out ASP.NET Web API.
Good Luck
EDIT
You can HTTP POST data to your MVC controller. If you do this then you can use a complex serialization format such as XML or JSON to send up your data. This will allow for the hierarchical nesting that your entities require.
I should have been more clear about web services. The type of operations you are performing seem like they might be better off in a web service. However regardless of whether or not you choose to use a web service your mvc controller can accept data can be correctly represented using HTTP POST actions.
I hope this helps.

Related

How to set {city} in all the routes MVC 5

I am developing a marketplace application which is supposed to have different products and vendors which are mapped to different cities.
The idea is that I need to store the CurrentCity in context so that I can use it to construct urls, filter data, fetch delivery areas etc.
e.g.
www.mywebsite.com/cityA/listings
www.mywebsite.com/cityB/listings
www.mywebsite.com/cityA/cart
www.mywebsite.com/cityB/cart
Something like the MoonPig website (https://www.moonpig.com/uk/Gift/Flowers/)
Currently, I am passing the city as a parameter in almost all the controller methods and also storing it in a cookie.
Alternatively, I am also thinking of creating a BaseController and possibly inject it in the OnActionExecuting(ActionExecutingContext context).
But the problem with the first approach is that all the Action methods need to have "city" as a parameter and I need to have it in context for doing something like
#Url.Action("Index", "Listings", new {city = cityName})
If I use the second approach, then I don't think I'll get the urls which have city in them.
I am ideally looking for a solution with which I can inject a city parameter as Base route / segment in the MVC RouteDictionary so that all the Urls are generated accordingly (with #url helper).
Is this possible or is there a better way to tackle this problem?
Would really appreciate if someone can show me a direction.
But the problem with the first approach is that all the Action methods need to have "city" as a parameter
This assumption is incorrect, since MVC automatically passes values from the current context when generating URLs, so there is no need to explicitly pass city as long as it is configured in the route and present in the URL. See this answer for how you can utilize this behavior for localization, which is similar to what you are doing.

ASP.NET MVC List of Web Components in Model

I need to get dynamically a list of Web Controls from View to be used in the model(dropdownlists, inputs, checkboxes, ...). Is it possible? I generate the controls in Razor.
My application should store last values of all controls for each user into the database and use them as predefined values for the next call of the form.
Any advice would be appreciated.
I need to get dynamically a list of Web Controls from View to be used in the model
First of all, you're probably going to find MVC a lot easier to understand if you abandon terms like "web controls" and the like. Your view, which may not may not be utilizing helpers to do so, is simply building HTML. Nothing more.
But more to the point, what you're proposing is exactly the opposite of what MVC does. Your model should have no knowledge of the structure of the view. (inputs, selects, other form elements, etc.) The model contains the data and business logic necessary to render the view. The view then uses that data and logic to build its interface.
You can post the values from the resulting HTML form to a server-side action. Then from that action you can store those values in a database or do whatever you like with them. If the key/value pairs of those values can logically be structured into the form of a model then the action can accept that model as a parameter, if not then it can also just as easily accept parameters for each individual value. (Though if you find yourself using a lot of parameters it would be better to build a simple view model just to encapsulate them.)
The order of operations is something like:
A request is made to a controller action.
That controller action invokes logic on a model and provides that model to a view.
The view binds its UI elements to the model's data and renders the interface.
The user interacts with the interface and uses it to perform a request to another controller action.
That controller action receives the data from that request, performs server-side logic, etc.
and so on...

ASP.NET MVC Web.Api Routing - Real world example

I have been looking at routing in the Web.Api and looking at various ways of representing endpoints. I came across Instagrams REST endpoints. (which has some lovely documentation) Using the Web.Api what would be the best way to set up the routing and controllers for a sitution like Instagrams user endpoints?
User Endpoints
GET/users/user-id Get basic information about a user.
GET/users/self/feed See the authenticated user's feed.
GET/users/user-id/media/recent Get the most recent media published by a user.
GET/users/self/media/liked See the authenticated user's list of liked media.
GET/users/search Search for a user by name.
If I wanted to replicate these endpoints in my app, how would I go about it. Would I just need one controller 'Users' with 5 methods, what kind of routing would I need to direct the REST calls to those methods?
I would structure this in a different way.
GET/user
GET/user/user-id
GET/user?q=user-name
GET/media
GET/media/user-id
GET/media/user-id?selection=recent
GET/media/user-id?selection=liked
GET/feed
GET/feed/user-id
This way you keep your Controllers for a specific target, much like keeping your classes for a single responsibility.
User
Media
Feed
When you use this approach it's much easier for a user to 'guess' the path. And I think you could already guess what each path does without any explanation. For me that's the most important when I'm designing a API.
GET/controller - always returns a item list
GET/controller/id - always returns a specific item
GET/controller?q= - always queries the controller
GET/controller?selection= - always selects a subset from the list off items
Ofcourse this is open for interpretation but it gives you an idea about how I would solve this particular problem and maybe some ideas to think about. Also have a look at this great book from Apigee - Web Api Designs
http://info.apigee.com/Portals/62317/docs/web%20api.pdf
Edit:
To make the routes you named I think you've got 2 (not very ideal) options.
Map a specific route for each url
Make a wildcard route
Option 1
I have not tried, or used this myself but you can find more info here:
Single controller with multiple GET methods in ASP.NET Web API
Option 2
If you go the wildcard route all requests with additional parameters will be routed to your default Get() method. In your get you have to look at the parameters using ControllerContext.Request.RequestUri.AbsolutePath or something like it and choose your actions on it.
config.Routes.MapHttpRoute(
name: "MyApi",
routeTemplate: "api/{controller}/{id}/{*wildcard}",
defaults: new { controller = "index", id = RouteParameter.Optional }
);

ASP.NET MVC - CMS Questions

I'm looking at developing an application that will include a CMS. I'm a seasoned web forms developer but only really just moving into MVC.
I have a couple of questions that I hope some of you guys can answer:
First, my current web forms CMS allows users to create a page, and then "drop" any number of user controls onto that page they have created. The way I do this is to create an entry in the DB together with the path and then use the LoadControl method.
I can see I can do this with partial views, but partial views have no code behind. If I've potentially got 100 controls that people can drop onto a page, does this mean that the ViewBag in the controller needs to cater for all 100 controls just in case they are used on the view? For example, a web forms user control will contain logic: rptItems.DataSource = blah; rptItems.DataBind()
With MVC, I'm assuming that logic will be in the view controller and the view would access it by the ViewBag? I'm a little confused at how to do this.
Secondly, how would you handle deep routing?
EG:
Store/Products/Category is fine, but what about Store/Products/Category/Delivery/UK ? Would I need to set up a route in global.asax for each route I need? In web forms, I just called the ReWritePath method and handled the routing myself using regular expressions.
Thanks for the time to read this, and hopefully answer some of my queries
For your second question, (ie, "deep routing"), you can handle this within your controller instead of adding real routes. Each part of the url is available via the RouteData.Values collection inside of your controller action. So, your route may look like
~/Store/Products/Category/{*params}
Assuming typical route configuration, this would call the Category(...) action method on ~/areas/store/controllers/storeController, which could then grap delivery and uk from the RouteData.Values collection.
There are a lot of other approaches to this - storing routes in a database and using associated metadata to find the correct controller and method - but I think this is the simplest. Also, it may be obvious, but if you really only need two parameters beyond 'Category' in your example, you could just use
public ActionResult Category(string category, string region)
{
...
}
and a route:
~/store/{controller}/{action}/{category}/{region}/{*params}
Delivery and UK would be mapped to the the category and region parameters, respectively. Anything beyond uk would still be available via the RouteData.Values collection. This assumes that you don't have more specific routes, like
~/store/{controller}/{action}/{category}/{region}/{foo}/{bar}/{long_url}/{etc}
that would be a better match. ({*params} might conflict with the second route; you'll have to investigate to see if it's a problem.)
For your first question:
You can dynamically generate the view source and return it as a string from the controller, eliminating the need to pass a lot of stuff via ViewBag. If a virtual page from your CMS database requires inclusion of partial views, you would add the references to those components when generating the page. (This may or may not address your problem - if not, please provide more information.)

asp.net mvc dynamic/relative routing

I'm developing a website which has a modular structure.
Every segment of the url presents an content item.
For example url: www.mysite.com/blogs/programming/2010/01/
Root item is 'blogs' of type 'area'. It has a child item 'programming' of type 'blog'.
Now there's '2010/01' left of the url.
Last valid (routable) item 'programming' was a blog so I need to map '2010/01' to action
BlogController.Date(int blogid, int year, int? month, int? day)
Every controller comes from a module (separate dll), which registers some item types (blog registers types 'blog' (routable) and 'post' (not routable). 'blog' can have children of type 'post').
When last valid (routable) item of the url is detected, logic knows which assembly and controller to look for. Now I need a way to invoke correct action with correct parameters.
Some routes for item of type 'blog'
{year}/
{year}/{month}
{year}/{month}/{day}
feed/
category/{category-name}/
tag/{tag-name}/
search/{*phrase}
{*post-name}
Any suggestions what would be a simple way to do the routing?
To solve the action parameter signature problem, I personally would create a new Model class "BlogModel" and have only that as your single parameter. This way, you'd have a consistent action parameter signature. However, this would require a bit more work, as you would need to create a custom ModelBinder object "BlogModelBinder" and register it to the ModelBinderFactory (or in MVC3 the DependencyResolver). In the "BlogModelBinder" you simply look up the RouteData's parameters and values and bind it to the corresponding field in your "BlogModel."
From my personal experience, I don't think there's an easy way to register your routes: you still would have to individually register the route urls to a specific action. Unless someone has an efficient way of registering the route urls, you can take solace in knowing that we all have to get our hands dirty with the plumbing code.

Resources