MVC3 Site Builder Application Design - asp.net-mvc

It would be great if someone could help me on this problem as I am new to web development and ASP.NET MVC. My goal is to design an MVC application which could help users to create their own mini websites. Each user will get their own mini websites and an admin panel to change the pages and template of their sites. I think there are scripts out there to achieve something similar, but we need to create our own for our specific requirements
Towards that goal I created a main mvc 3 application and I created an area as Site under it. We want the user to have their own sub domain urls like www.site1.mainsite.com where site1 is the name of the mini site of the user. For that I added 2 routes one for the main site and another for the area like
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "MainSite.UI.Controllers" }
);
In Area Registration
context.MapRoute(
"Sites_default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new {controller = new SiteConstraint()},
new[] { "MainSite.UI.Areas.Site.Controllers" });
Both of them have the same url type, but area route have a constraint where I added like
var url = httpContext.Request.Headers["HOST"];
var storeName = url.Split('.')[1];
var match = storeName != "mainsite";
return match;
so far its working, But i dont think its a good design. Now apart from this I need to add 2 more areas one is siteadmin and another is blog.
What should be best way to achieve this?
sub domain urls should be directed to area "site"
subdomain url/Admin should be routed to area "siteadmin"
domain/blog should be routed to blog area.
domain other urls should be handled by main controllers
Thanks in advance

I didnt get even one reply :( But I tried playing with the routes and achieved some what near to what I was looking for.
Thanks to Route Debugger, It helped me to see what was going wrong with the routes
When I tried adding another area "Admin", and registered it with the route below
context.MapRoute(
"SiteAdmin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional },
new { controller = new SiteConstraint() },
new[] { "MainSite.UI.Areas.SiteAdmin.Controllers" }
);
Things started going wrong. Route Debugger helped to me to figure out, what was happening.
For me the order of the Routes really mattered, so I had to make sure that my areas were registered in the correct order.
AreaRegistration.RegisterAllAreas()
was not registering my routes in the correct order, Googling gave me this helper method.
public static class RouteHelper
{
public static void RegisterArea<T>(RouteCollection routes, object state) where T : AreaRegistration
{
var registration = (AreaRegistration)Activator.CreateInstance(typeof(T));
var context = new AreaRegistrationContext(registration.AreaName, routes, state);
var tNamespace = registration.GetType().Namespace;
if (tNamespace != null)
{
context.Namespaces.Add(tNamespace + ".*");
}
registration.RegisterArea(context);
}
}
That method helped me to register the areas in the required order in the Global asax as
RouteHelper.RegisterArea<SiteAdminAreaRegistration>(RouteTable.Routes, null);
RouteHelper.RegisterArea<SiteAreaRegistration>(RouteTable.Routes, null);
I am writing it down, what I got so far as an answer so that someone else may be find it useful.

Related

Dynamic Mvc route

I know there is a lot topics with the same name, but none of those I've found so far, have provided the answer I need.
I'm creating a "dropbox" site for my students to upload their projects on when they have to submit them to me.
The thing is that the students are able to create folders and I would like(if possible) to display the url based on the "folders" they are able to create on the website.
Folders could look like.
Home
Project
Project 1
Project 1 v1
Project 1 v2
And so on.
project 2
Submit
Basic the path could be short or long depends on the folder level.
Like : Home or Project/Project-1-v1/ or Project/Project-1-v1/Upload/Final
Is there a way to create a dynamic url/action that accept any of the above Path's ?
This can be done fairly easy.
My RouteConfig.cs
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute("CatchALL", "{controller}/{action}/{id}/{*catchall}",
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
My Controller
[Route("Home/{*catchall}")]
public ActionResult Part(string catchall)
{
return View();
}
If this was the url: http://stackoverflow.com/Home/36542665/dynamic-mvc-route catchall would contain = 36542665/dynamic-mvc-route

MVC routing with optional first parameter

I am trying to get a route like:
{lang:optional}/{controller}/{action}/{id:optional}
With "controller constraints" idea from this article: (MVC Routing Constraint on Controller Names), the above route works very well, when {lang} value is presented or not.
However I have a problem to match this route:
routeName: testRoute
url: {lang:optional}/list/{something:optional}
{controller = "product", action = "index"}
for the above route, the {lang} value must be presented, otherwise it does not work.
I have two workarounds to work it out.
The first way is to set two routes for the above:
The very standard one:
routeName: testRoute
url: /list/{something}
{controller = "product", action = "index"}
and another very standard one:
routeName: testRouteLang
url: {lang:not_optional}/list/{something:optional}
{controller = "product", action = "index", lang="de"}
I am wondering if there is a way to combine the two standard routes into one single route.
The second workaround is to use subdomain name, such as
http://example.com/list (default to English)
http://de.example.com/list (de)
But I really do not like the subdomain idea for the SEO reasons (maybe I am wrong on this point).
My goal is to remove the default "en-us" in the URL. I like this
http://www.example.com/list/something (default as English)
I do not want to force "en-us" in the url
http://www.example.com/en-us/list/something
The "lang" should only be presented in the Url if the current culture is not English:
http://www.example.com/de
http://www.example.com/fr/list/something
Thanks.
Finally I found an very easy and DRY solution. The core thing is to use the HttpContext.Current.RewritePath to inject the default "en", while this "en" will not be shown in the URL.
protected void Application_BeginRequest()
{
var rawUrl = HttpContext.Current.Request.RawUrl;
var segments = HttpContext.Current.Request.Url.Segments;
var segment1 = segments.Count() >= 2 ? segments[1] : string.Empty;
if (IsSomethingThatIWantToHandle("are,you,js,script,css,jpg,png,and,so,on?")
&& !LittleHelper.DoIHaveValidLangAlready(segment1))
{
HttpContext.Current.RewritePath("/en" + rawUrl);
}
}
When generate URL, if lang is null/empty, the URL will be have a double //. I just need a little helper to remove the extra "/".
When define a route, a trick is that the area name must be added to the DataTokens, otherwise the view cannot be correctly located, if areas are presented in the project.
routes.MapRoute(
"good name",
"{lang}/some-cool-stuff/{id}/{slug}",
defaults: new { area = "bigarea", controller = "bigcontroller", action = "tinyaction",
lang = UrlParameter.Optional, id = UrlParameter.Optional, slug = UrlParameter.Optional }
, constraints: new { lang = new CultureConstraint() }
).DataTokens.Add("area", "bigarea");
The CultureConstraint is very straightforward -- just verify whether it is a valid culture name. The namespace constraints is not necessary. However if the route table is big and complicated then the controller constraints, or even action constraints is very necessary otherwise duplicated routes will be an issue.
My default route in my project now is:
url: "{lang}/{area}/{controller}/{action}/{id}/{slug}",
and it works beautifully as I want.

MVC Routing Redirection

I have a webservice project that is being expanded to host multiple APIs instead of just one, so I wanted to cleanup the routes without breaking the old version. The main API used to sit off of a controller named API and accept parameters like this:
api/{language}/{action}/{*parameters}
Now, I have an Area named API that is going to house each of the APIs in their own controller and the route will look like this:
api/{controller}/{language}/{action}
I still need the old route to be usable for legacy apps already using the old route, I was hoping just to be able to create a 2nd "legacy" route that would catch the old pathing and use the new controller. I tried this but it only worked then with the new location and the ikd one returned a 404.
api/{language}/{action}/{*parameters}",
new { controller = "api1", action = "Index", language = "json" }
Any ideas on how to accomplish this? I tried RouteMagic but it didn't seem to work. Maybe I set the redirect up wrong though.
Ok, figured out after getting a sandwhich...just needed to add route constraints so that the old version would not catch on the new route format:
routes.MapRoute(
"API_default",
"api/{controller}/{language}/{action}",
new { controller = "api1", action = "Index", language = "json" },
new { language = "json|xml" }
);
routes.MapRoute(
"API_legacy",
"api/{language}/{action}/{*parameters}",
new { controller = "api1", action = "Index", language = "json", parameters = UrlParameter.Optional },
new { language = "json|xml" }
);

Having issue with multiple controllers of the same name in my project

I am running into the following error with my ASP.NET MVC 3 project:
Multiple types were found that match
the controller named 'Home'. This can
happen if the route that services this
request ('Home/{action}/{id}') does
not specify namespaces to search for a
controller that matches the request.
If this is the case, register this
route by calling an overload of the
'MapRoute' method that takes a
'namespaces' parameter.
The request for 'Home' has found the
following matching controllers:
MyCompany.MyProject.WebMvc.Controllers.HomeController
MyCompany.MyProject.WebMvc.Areas.Company.Controllers.HomeController
I have a HomeController in my default controller folder, with a class name of MyCompany.MyProject.WebMvc.Controllers.HomeController.
My RegisterRoutes method, in my global.asax, looks like:
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 } // Parameter defaults
);
}
I then have an area called Company, with a HomeController in the default controller folder for the area, with a class name of MyCompany.MyProject.WebMvc.Areas.Company.Controllers.HomeController.
The RegisterArea method in the CompanyAreaRegistration file looks like:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Company_default",
"Company/{controller}/{action}/{id}",
new { area = "Company", action = "Index", id = UrlParameter.Optional }
);
}
This is all leading the error I highlighted at the beginning of this post. I am struggling trying to piece together a solution from various other posts, with NO LUCK.
Is it possible to have a HomeController in the default controllers folder and then one in EACH area? If so, do I need to make (assuming I do) changes to my configuration file to make this work?
Any help would be much appreciated!
The error message contains the recommended solution: "If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter."
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "MyCompany.MyProject.WebMvc.Controllers"}
);
This will make http://server/ go to your HomeController's Index action which is, I think, what you want. http://server/company/home will go to the Company area's HomeController's Index action, as defined in the area registration.
This is the asp.net mvc4 approach:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "RegisterNow", id = UrlParameter.Optional },
namespaces: new[] { "YourCompany.Controllers" }
);
I had renamed the namespaces, so, i only delete de folders bin and obj and rebuild, work again.
If you're using RazorGenerator, just informing the namespaces parameter could be not enough.
I got to solve adding the statement marked below at Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ControllerBuilder.Current.DefaultNamespaces.Add("MyProject.Controllers"); // This one
}
Another plausible cause of this issue could be found below:
Multiple types were found that match the controller named 'Home'
use this
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "ProjectName.Controllers" }
);
Use only the name of the project:
Public Class RouteConfig
Public Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
routes.MapRoute( _
name:="Default", _
url:="{controller}/{action}/{id}", _
defaults:=New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional} _
, namespaces:={"MvcAreas"})
End Sub
I had the same issue and found that the older version had created compiled files in the "bin" folder.
Once I deleted these the error disappeared.
As Chris Moschini mention the namespaces parameter may not be enough if you have two areas with same controller name with different namespaces and the default none area route will return 500 server error.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "MyCompany.MyProject.WebMvc.Controllers"}
);
It "best" to override the default route handler and add this line:
RequestContext.RouteData.DataTokens["UseNamespaceFallback"] = false;
I had this issue after I added a reference to another project that had the same routes and the problem continued after I removed the reference.
Resolved by deleting the .dll file of that added reference from the bin folder and rebuilding.
Like many others, I had this problem after creating a new MVC template project from VS2017 menu, on building the project I would get the op's error message. I then used the answer https://stackoverflow.com/a/15651619/2417292 posted earlier in this thread by cooloverride
for mvc4 projects.
This still didn't fix my issue so I renamed my Home view folder and HomeController file to be the view folder Company/ and controller file CompanyController. This then worked for me, not a fix per say but a workaround if your not stuck on having the route Home/Index my other issue was I couldn't find the reference causing the error and due to my dev platform being Azure WebApplication vs a full VM with a complete file system and IIS settings to mess with.
For MVC5 below code works for issue same controller name for different areas. You have to specify which area controller have to hit first.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "ApplicationName.Areas.AreaName.Controllers" }
).DataTokens.Add("area", "AreaName");
This issue occurred when I accidentally added
[Route("api/[controllers]s")]
instead of
[RoutePrefix("api/[controllers]s")]
to the top of my ApiController.

Is it possible to run ASP.NET MVC routes in different AppDomains?

I am having problems with thinking up a solution for the following. I got a blog which I recently upgraded from web forms to MVC. The blog is avalible in both swedish and english on two different domains and are running in the same web site in IIS.
The problem is that I would like language specific urls on the both sites, like this:
English: http://codeodyssey.com/archive/2009/1/15/code-odyssey-the-next-chapter
Swedish: http://codeodyssey.se/arkiv/2009/1/15/code-odyssey-nasta-kapitel
At the moment I have made this to work by registering the RouteTable on each request depending on which domain is called. My Global.asax Looks something like this (not the whole code):
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
string archiveRoute = "archive";
if (Thread.CurrentThread.CurrentUICulture.ToString() == "sv-SE")
{
archiveRoute = "arkiv";
}
routes.MapRoute(
"BlogPost",
archiveRoute+"/{year}/{month}/{day}/{slug}",
new { controller = "Blog", action = "ArchiveBySlug" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
routes.MapRoute(
"404-PageNotFound",
"{*url}",
new { controller = "Error", action = "ResourceNotFound" }
);
}
void Application_BeginRequest(object sender, EventArgs e)
{
//Check whcih domian the request is made for, and store the Culture
string currentCulture = HttpContext.Current.Request.Url.ToString().IndexOf("codeodyssey.se") != -1 ? "sv-SE" : "en-GB";
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(currentCulture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(currentCulture);
RouteTable.Routes.Clear();
RegisterRoutes(RouteTable.Routes);
Bootstrapper.ConfigureStructureMap();
ControllerBuilder.Current.SetControllerFactory(
new CodeOdyssey.Web.Controllers.StructureMapControllerFactory()
);
}
protected void Application_Start()
{
}
This works at the moment but I know it not a great solution. I have been getting a "Item has already been added. Key in dictionary" error when stating up this app and it does not seems stable at times.
I would like to only set up my routes in the Application_Start as they should and not having to clear them on every request like I am doing now. Problem is that the request object does not exist and I have no way of knowing which of the language specific routes I should register.
Been reading about the AppDomain but could not find many examples on how to use it on a web site. I'we been thinking to star something like this:
protected void Application_Start()
{
AppDomain.CreateDomain("codeodyssey.se");
AppDomain.CreateDomain("codeodyssey.com");
}
Then registring each web sites routes in each app domain and send the requests to one of them based on the url. Can't find any examples on how to work with AppDomains in this manner.
Am I completely off track? Or is there a better solution for this?
The ASP.Net runtime manages AppDomains for you, so its probably not a good idea to create AppDomains in your code.
However, if you can, I would suggest creating multiple IIS Applications (one for http://codeodyssey.com and one for http://codeodyssey.se). Point both applications at the same directory on disk. This will give you the two AppDomains you are looking for.
Then, in your Application_Start code, you can check the domain and build routes accordingly.

Resources