Organizing (splitting?) controller while maintaining URL - asp.net-mvc

We have a large web application that is a mix of WebForms and MVC. The URLs we work with look as such www.something.com/Secure/Module/SomeAction. When we used to work with Webforms Module was a folder, as we had as many ASPXs as we needed under that folder. With MVC we're taking a similar approach, where Module translates to a controller, containing all the action. The problem that we're running into is that if the Module has something like 20 - 30 actions it's getting really messy. For example if we have a PersonReport, that usually translates to several actions dedicated to serving that report alone (to facilitate ajax calls etc). Our actions are fairly thin, they populate the model (usually calling WCF services) and that's about it. Nevertheless it can easily creep up to 1500 lines of codes, and we start utilizing regions to organize sections of the controller. Creating a new controller (by default) will obviously stray away from our URL pattern. Any suggestions on how to better organize this mess?

You can use attribute routing if you plan to use MVC 5. This will allow you to specify custom routes for each Action method with attribute
The previous versions allow you to override the default routes through RouteConfig.cs file. Example would be
routes.MapRoute(
name: “ProductPage”,
url: “{productId}/{productTitle}”,
defaults: new { controller = “Products”, action = “Show” },
constraints: new { productId = “\\d+” }
);
See this link for more info.
Hope this helps.

Related

Using custom routes instead of /controller/action/{id}

I have to make vanity urls for an already existing site. The requirements are:
The existing controllers are a kind of hierarchical structure and can't go away or be overridden in any way. Examples: domain.com/Dog/vanity and domain.com/Cat/vanity.
Keep existing actions. Any existing actions must take priority so that the page for that action is not stomped on by the vanity url.
take future pages and actions into account so that the above requirement is met (a new vanity url is ignored and the action/view executed instead)
To date, I have tried various solutions with routing that allow me to have domain.com/vanity which is nice but the marketing guys don't like because of the different departments within the company. I've tried routing which will override the existing actions and treats them all as vanities (also not feasible). I've a solution in place that programmatically deals with the url that was requested and redirects to a page that actually exists but this is not scalable in any way.
So far, I know that the vanity portion can be treated as a parameter to the action so that I can fire off the default page in the route (Index) but this is, so far, doesn't preserve the structure.
TL;DR: I need to have a solution that allows for domain/controller/vanity structure while also allowing domain/controller/action
Using AttributeRouting for MVC4 you can accomplish a working solution until you ramp up the replacement project. It'll allow you to keep existing routes while adding new, custom ones with little impact.
[Route("my-vanity/is-cool/post/{id}")]
public ActionResult Index(int id)
{
}
The important part is to remember priority, so you write routes that don't overwrite/are overwritten by existing routes. You can steer this to some degree with properties on the attribute. Below is just an example that will put the added route last in priority for the entire site.
[Route("my-vanity/is-cool", SitePrecedence = -1)]
public ActionResult Index()
{
}
ASP.NET WebApi2 have built in support for attribute routing. With it you can define URL's in whatever way you like (instead of following the /controller/action pattern)
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

How is a # used by the MVC code of the controller and views

URL : http://www.myserver.com/movies/details/19
Can someone tell me how the number '19' in the URL is used by the MVC code for the controller and views
(I already know how it relates to a particular representation of the domain data)
If you look at the default route configuration, you'll see that it is built as "/{controller}/{action}/{id}". If you stick with that and use (int id) as your parameter for the action, that is the value passed into id.
Edit: And just in case you need to find that, you can look in the App_Start folder for the RouteConfig.cs file.
What the various pieces of the URL do depend on how your Routes and Controllers are set up. The asp.net website provides a series of tutorials designed to introduce you to the options:
http://www.asp.net/mvc/tutorials/controllers-and-routing

Keeping hierachy in ASp.NET MVC - Use Areas or just routing?

I am new to ASP.NET MVC4.
I must build a number of utilities (~40-50) that are going to be used by 4 groups of users in our internal windows domain.
I have made a controller named SupportController and i plan to include in there the 40 actions i want.
Some of them are special to each group and some are common.
I want to create a frontpage by accessing HomeController -> Index action. This frontpage will include a global menu.
I want to have urls like:
www.mydomain.com/bg/nullfix
www.mydomain.com/bg/invoicecorrection
www.mydomain.com/ro/updatecurrency
www.mydomain.com/ro/deletecosting
www.mydomain.com/common/fixaddressbook
e.t.c
From what i have read i do this url structure by altering the route configuration, For example (covers the above example #1 url):
routes.MapRoute(
name: "BgNullifiedInvoice",
url: "bg/nullfix",
defaults: new { controller = "Support", action = "BgNullifiedInvoicesFix"}
);
Since this will be my entry level mvc project, it will grow over time...
So is it proper to get into using the Areas functionality of ASP.NET MVC or get by using just routing?
Personally I would suggest using areas in such a project. They exist to separate your software's functionality into modular, organised and easily maintainable groups each with their own set of routes - which is exactly what it sounds like you need.
One thing that's certain about software is change, and areas help ease that change.

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 How many levels deep should a view or URL be?

I am still learning ASP.NET MVC. With webforms, I would create a new folder let's call it admin. In there I might have many pages for create_product, edit_product, etc. So the URL might look like http://somesite.com/admin/create_product.aspx.
But with MVC it is a little different. I am trying to see what would be the best way to do this.
Would doing http://somesite.com/admin/product/create be right? Or should it just be http://somesite.com/product/create? If I do it as the first way, do I put everything in the "admin" controller or should it be separated into a "product" controller?
I know this is probably subjective or personal choice, but I would like to get some advise.
Thanks.
Part of the benefit of ASP.NET MVC (and more generally, the URL Routing Engine common to all of ASP.NET in .NET 3.5 SP1) is that the URLs can be flexibly configured to map to any folder / file structure you prefer. That means it's much easier than it was in the days of WebForms to modify your URLs after you've started building your project.
To your specific questions:
One Admin Controller vs. Product Controller - In general, the guidance is to keep controllers focused so that they are easier to test and maintain. For that reason, I would suggest using a single controller per object type (like Product) with your CRUD actions. Examples in your case:
/admin/product/create
/admin/product/edit/34 or /admin/product/edit/red-shoes (if name is unique)
In either case, the Create, Edit, Deatils actions will all be in the ProductController. You may just have custom routes for the "admin actions" (like Create and Edit) that limit their usage (and add the "admin" text to the URL), and then the Details action would be usable by all visitors to your site.
Securing Admin Views - One important fact to remember with MVC: all requests go directly to controllers, not views. That means the old "secure a directory with web.config" does not apply (usually) to MVC for securing your Admin. Instead, you should now apply security directly to the controllers. This can easily be achieved by using attributes to Controller classes like:
[Authorize] - Just checks that the user is logged-in
[Authorize(Roles = "Admin")] - Limit to specific user roles
[Authorize(Users = "Joe")] - Limit to specific users
You can even create a custom route for "Admin" views in your site and limit access to those views by enforcing your authorization check in the URL routing, like this:
routes.MapRoute(
"Admin",
"Admin/{controller}/{action}",
new { controller = "Product", action = "Index" },
new { authenticated= new AuthenticatedConstraint()}
);
Where AuthenticatedConstraint looks something like:
using System.Web;
using System.Web.Routing;
public class AuthenticatedConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return httpContext.Request.IsAuthenticated;
}
}
Good details on Stephen Walther's blog:
ASP.NET MVC Tip #30 – Create Custom Route Constraints
For admin stuff, just mark with [Authorize] attribute. To ensure only admins can use it, do something like [Authorize(Roles = "Admin")]. Check out this question
Also, /product/create is most common, I think :)
I3Dx definitely has the right guidance for the Authorize attribute, this is essential for keeping controller secure, you can apply to a controller or individual actions.
As far as the URL depth, I would not worry about the depth, I would be more concerned that the route made logical sense for example:
domain.com/admin/products/edit/1
domain.com/admin/groups/edit/1
domain.com/products/view/1
domain.com/groups/view/1
This way you know what is happening with each route. it is obvious that one is an admin and one is an end user.
The easyest way to check is to get someone to read your URL and ask them what they would expect to see.
Hope this helps.
OH and one last thing, for client side routes we often use "slugs" rather than ids so that it is more readable. So when someone creates a product we slugify the name so it can be used in the route such as:
domain.com/products/view/big-red-bucket
rather than
domain.com/products/view/1

Resources