MVC 3 - multiple controllers or controller with multiple actions plus routing - asp.net-mvc-3

I'm putting together a simple enough brochure site and decided to use MVC3 as a learning opportunity. Content of certain sections of the website will be stored in a DB and can be updated by an admin via a simple GUI. I decided not to use a prebuilt CMS again to learn how to do database operations in this language which is new to me.
I want a very simple URL structure:
foo.com (home)
foo.com/bio
foo.com/news
foo.com/about
foo.com/events
etc
The straightforward way to achieve that is to have a controller for each page, and use the Index() ActionResult of each controller.
Is it OK / best practice to have a controller for each of these pages of the site? News and Events won't have subpages, but might have paging, with the URL looking something like
foo.com/news/
foo.com/news/page2
foo.com/news/page3
foo.com/news/page4
If I had a single controller, and used multiple actions, the URLs by default look like
foo.com (home)
foo.com/home/bio
foo.com/home/news
foo.com/home/about
foo.com/home/events
Which would then have me updating the routing to achieve what I want.

With respect to the use of controllers, I think the best practice is to have a particular controller for a domain of action or domain of content, not by the hierarchy of your main menu.
For example:
Your main page:
foo.com
Controller -> Home
Method -> Index
Parameter -> null
foo.com/news/2
Controller -> news
Method -> Index
Parameter -> 2
But Home and News are two different "equal" controllers, it just happens that the first controller the user interacts with is the Home controller.

In the case you want to use a single controller, you might try,
routes.MapRoute(
name: "Default",
url: "{action}/{page}",
defaults: new { controller = "Home", action = "Index", page = UrlParameter.Optional },
constraints: new { page = new PagingConstraint() }
);
as route defination. And to only allow paging for "news" and "events", use a custom route constraint as,
public class PagingConstraint : IRouteConstraint
{
private static readonly string[] PagingEnabledActions = new string[] { "news", "events" };
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return string.IsNullOrEmpty(values[parameterName].ToString()) || new Regex(#"^[Pp][Aa][Gg][Ee][0-9]+$", RegexOptions.Compiled).IsMatch(values[parameterName].ToString())
&& PagingEnabledActions.Contains(values["action"].ToString().ToLower());
}
}
Hope this helps.

Related

ASP.NET MVC Routing - Thousands of URL's

My ecommerce application stores URL's for item pages in the database. There are thousands of these URL's, which are all root level (i.e. domain-name.com/{item-page-url}).
If I add all of these URL's to the route table by using a simple for loop to call RouteCollection.MapRoute for each URL site performance degrades exponentially. Why? The reason for this is here.
How should I properly handle this situation? Adding all of the routes to the route table doesn't seem right (not to mention the performance pretty much confirms that). I've seen a few ideas about inspecting all incoming URL's and then trying to match that to the URL's in the database but don't fully understand how I'd implement that, nor am I sure if it's the best approach.
Any ideas or suggestions? This seems like it would be not so uncommon, but I haven't found a concrete way to handle it.
If you can change your route to
mycreativeshop.com/product/my-product-name then adding following route to the top of your route config file can help you.
routes.MapRoute(
"CustomRouteProduct",
"product/{id}",
new { controller = "yourcontrollername", action = "Index" }
);
and in the action map the parameter value with name of your product name
public ActionResult Index(string id)
{
//var prdName = id.Replace("-", " ");
//look up prdName in database
return View();
}
Update
Added following as a top route
routes.MapRoute(
"CustomRouteProductZX",
"{id}",
new { controller = "Content", action = "Index" }
);
and by accessing http://localhost:12025/Car-Paint I was directed to Index action of ContentController where I accessed "Car-Paint" in parameter id
But, above having above blocks patterns like http://localhost:12025/Home/ (here Home is also treated as a product)

Dynamic Routing in ASP.NET MVC 3

I am building a blog engine using MVC 3 and razor. In this scenario, I have given options like a user can have multiple blogs (similar to blogger.com)
Now say a user 'yasser' has the following 3 blogs
TechStory
GameGeek
MeMyStory
so I want all other users to access these blogs by the following urls
www.domainName.com/blogs/TechStory
www.domainName.com/blogs/GameGeek
www.domainName.com/blogs/MeMyStory
And more blogs can be added hence more such url will be acessed in future.
I know that something needs to be done with Routing, but being new to MVC dont seems to get it. Please can some one guide me on this.
Add this route on top of your Default one:
routes.MapRoute(
"Blog",
"Blogs/{blogName}",
new { controller = "Blogs", action = "Index" }
);
Your controller will look like this:
public class BlogsController : Controller
{
public ActionResult Index(string blogName)
{
BlogModel model = // find blog by blog name
return View(model);
}
}
Also, one suggestion: Keep your controller names in singular mode: BlogController instead of BlogsController. Change URL and Routing accordingly if you decide to do so.

MVC3 MapRoute to convert aspx query params into the route

I'm working on a project to rewrite an aspx site as MVC3. I want to make the old URLs work on the new site. I have named my controllers and actions such that the URLs actually contain enough info in the query string to route correctly but I'm having trouble getting the routing to work since it doesn't like the ? in the URL.
Basically I have old URLs like this:
www.example.com/Something/SomethingElse/MyPage.aspx?Section=DetailSection&TaskId=abcdef
I tried to create a route using:
routes.MapRoute(
"OldSite",
"Something/SomethingElse/MyPage.aspx?Section={action}Section&Id={id}",
new { controller = "Task", action = "Index", id = UrlParameter.Optional }
);
I want it to route to the correct new URL which is:
www.example.com/Task/Detail/abcdef
I know that all traffic to the MyPage.aspx page should go to my new Task controller and the beginning of the Section parameter always matches one of a few corresponding actions on that controller.
Unfortunately I have found that I get an error that a route can't contain a question marks. How should I handle this? Would it be better to use URL rewriting? Because this is a private site I'm not concerned with returning permanent redirects or anything - no search engine will have links to the site anyway. I just want to make sure that customers that have a URL in an old email will get to the right page in the new site.
In this one case I think the simplest way would be to have your old page mapped to a route:
routes.MapRoute(
"MyPage",
"Something/SomethingElse/MyPage.aspx",
new { controller = "Task", action = "MyPageHandler" }
);
And have this route mapped to an action method in TaskController:
public ActionResult MyPageHandler(string section, string taskId)
{
if (section.Contains("Detail"))
{
// execute section
}
}
This way you're treating your old site's query string for what it is: a query string. Passing those parameters straight into an action method is the most MVC-y way to interpret your old site.

ASP.NET MVC3 sample project removing home folder

I'm using the ASP.NET MVC3 sample project and would like to have new links added to the page that go directly to the root url
So instead of mydomain.com/Home/About it would do mydomain/About.
This page suggests adding a new route. http://weblogs.asp.net/gunnarpeipman/archive/2011/04/17/asp-net-mvc-defining-short-urls-for-root-level-pages.aspx
Is there another way? Say I have 5 pages that will be on the root do I have to add a special route for each one?
Under the assumption that you are looking to do a bunch of single path requests/respones, and not just redirect home controller actions, then this is an option.
The link you provide is one way to do that. The other is to create 5 controllers using the default route. I'm not sure if I would suggest either is better (due to a lack of what your 5 paths actually are), but they both produce the same out come. If your default route looks like:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
It's basically stating that the default controller is home and the default action is index. These values are not mutually inclusive, meaning that neither required the other in order to be a default value.
Thus you could do:
website.com/about with
public AboutController
{
public ActionResult index()
{
return this.View();
}
}
and/or website.com/people with
public PeopleController
{
public ActionResult index()
{
return this.View();
}
}

MVC3 Area +Authorize attribute + Role strange issue

I really don't know what title should I use to describe my problem. To simplify my problem. Here is my test. I create a mvc3 site from scratch. I then add area called "admin". Inside admin, I have a controller named "Search" and has "Authorize" attribute decorated. I then changed my Global.ascx.cs route setting to append my controller namespace. Now I start my test.
Question 1
When I am accessing to http://localhost:xxx/Search page, it redirects me back to /Account/Logon page, it makes me confuse first, why it redirects me to logon page? it shouldn't reach to Admin search controller at all as I understand. If I removed the Authorize attribute, it display the yellow screen said can't find the view as I expected.
Question 2
If I add Authorize attribute with role, e.g. (Roles="Admin"), then I try access to Search page again, no matter login succeed or not, I always get redirect back to logon page. Why it doesn't give me the yellow screen, coz I am trying to request the search controller index view in the main site not the admin area's one. quite confuse.
I am a newbie in MVC development, can someone give me a solution regarding to my problem?
Thanks
Global.ascx.cs
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[]{"TestAreaRouting.Controllers"}
);
}
You could constrain the default controller factory to look only inside the specified namespace for controllers in the RegisterRoutes method of Global.asax by setting the UseNamespaceFallback data token to false:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "TestAreaRouting.Controllers" }
).DataTokens["UseNamespaceFallback"] = false;
}
If you don't do this when you request /search the admin area route doesn't match because the url doesn't start with the Admin prefix.
So it is the default route that matches. The default controller factory starts scanning the assembly for a class called SearchController that derives from Controller and since it finds one it instantiates it and uses it to serve the request. Obviously it doesn't find a corresponding Index view because it looks in ~/Views/Search/Index.cshtml which obviously doesn't exist. The actual view is located in the area.
Now that we have constrained the controllers to their respective locations you could decorate them with the Authorize attribute and it should behave consistently.

Resources