Multi-lingual routes for areas in ASP.NET MVC - c#

I've got an issue with ASP.NET MVC routing for areas. I set up the routing with language without areas, and it is working fine.
Myrouteconfig.cs:
routes.MapRoute(
name: "LocalizedDefault",
url: "{lang}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { lang = "ar-ag|en-us" }
);
In areasregistration class I setup the routes like this:
context.MapRoute(
"Exhibitor_default",
"{lang}/AreasName/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
I also created razorviewengineclass method for setup language currentculture.
This is my razor view engine class method for setup the routes
public void SetCurrentCulture(string lang)
{
_currentCulture = lang;
ICollection<string> arViewLocationFormats =
new string[] { "~/Views/{1}/" + lang + "/{0}.cshtml", };
ICollection<string> arBaseViewLocationFormats = new string[] {
#"~/Views/{1}/{0}.cshtml",
#"~/Views/Shared/{0}.cshtml"
};
this.ViewLocationFormats = arViewLocationFormats.Concat(arBaseViewLocationFormats).ToArray();
}
I try this for areas routes. I have multiple areas with different names and the same controller.
ICollection<string> arViewLocationFormats =
new string[] { "~/Views/{1}/" + lang + "/{0}.cshtml",
"~/"+ lang + "/Areas/Views/{1}/{0}.cshtml"
};
ICollection<string> arBaseViewLocationFormats = new string[] {
#"~/Views/{1}/{0}.cshtml",
#"~/Views/Shared/{0}.cshtml",
#"~/"+lang+"/Areas/Views/{1}/{0}.cshtml",
#"~/"+lang+"/Areas/Views/Shared/{0}.cshtml"
}
I am getting this error
Multiple types were found that match the controller named 'Dashboard'

Related

Asp.net Mvc Route By Only Slug

I'm using ASP.NET MVC 4 with C#. I'm using areas and it's named like "Admin"
Here is my route config;
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(name: "PageBySlug",
url: "{slug}",
defaults: new {controller = "Home", action = "RenderPage"},
constraints: new {slug = ".+"});
routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional},
namespaces: new[] { "Web.Frontend.Controllers.Controllers" });
}
}
I generated frontend page links like; "products/apple-iphone"
So I want to call them like this.
But the error is: The code can't get the controller / action method.
I used frontend page links like;
#Html.ActionLink(linkItem.Title, "RenderPage", routeValues: new {controller = "Home", slug = linkItem.PageSlug})
#Html.RouteLink(linkItem.Title, routeName: "PageBySlug", routeValues: new { controller = "Home", action = "RenderPage", slug = linkItem.PageSlug })
#linkItem.Title
#linkItem.Title
They are rendering url links like; http://localhost:1231/products/apple-iphone
It's like what I want. But when I click any link, asp.net mvc gives me this error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /products/apple-iphone
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1069.1
Here is my controller;
namespace Web.Frontend.Controllers
{
public class HomeController : BaseFrontendController
{
public ActionResult Index()
{
return View();
}
public ActionResult RenderPage(string slug)
{
return View();
}
}
}
So how can I catch every link request like this combined slug and turn my coded view ?
The problem is, When you request products/iphone, the routing engine don't know whether you meant the slug "products/iphone" or the controller "products" and action method "iphone".
You can write a custom route constraint to take care of this. This constraint will check whether the slug part of the urls is a valid controller or not, if yes,the controller action will be executed.
public class SlugConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection)
{
var asm = Assembly.GetExecutingAssembly();
//Get all the controller names
var controllerTypes = (from t in asm.GetExportedTypes()
where typeof(IController).IsAssignableFrom(t)
select t.Name.Replace("Controller", ""));
var slug = values["slug"];
if (slug != null)
{
if (controllerTypes.Any(x => x.Equals(slug.ToString(),
StringComparison.OrdinalIgnoreCase)))
{
return false;
}
else
{
var c = slug.ToString().Split('/');
if (c.Any())
{
var firstPart = c[0];
if (controllerTypes.Any(x => x.Equals(firstPart,
StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
}
return true;
}
return false;
}
}
Now use this route constraint when you register your custom route definition for the slug. make sure you use {*slug} in the route pattern. The * indicates it is anything(Ex : "a/b/c")(Variable number of url segments- more like a catch all)
routes.MapRoute(name: "PageBySlug",
url: "{*slug}",
defaults: new { controller = "Home", action = "RenderPage" },
constraints: new { slug = new SlugConstraint() }
, namespaces: new string[] { "Web.Frontend.Controllers.Controllers" });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
, new string[] { "Web.Frontend.Controllers.Controllers" });
you can provide only this type of link
#linkItem.Title
Because Routetable find your route using Route name provided by you. so controller name and action name is not necessary.

Missing something simple in getting mvc route to work

It's late, I've had a lot happen today and I must be missing something very simple.
I have a route such as this:
routes.MapRoute("RequestKey", "License/RequestKey/{apello}/{requestcipher}",
new { controller = "ProductKey", action = "RequestKey" },
new { apello = "", requestcipher = "" },
new[] { "....Controllers" }
My controller action:
[ChildActionOnly]
public string RequestKey(string apello, string requestcipher)
{
return "Yeah";
}
And the url doesn't hit the controller action....time for bed?
http://localhost:53764/License/RequestKey/qwerqewrqwr/zxcvzcvzcx
Your tags indicate that you are using ASP.NET MVC 4, then try this route mapping:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{param1}/{param2}",
defaults: new { controller = "Home", action = "Index", param1 = UrlParameter.Optional, param2 = UrlParameter.Optional }
);
If you have the possibility to upgrade to ASP.NET MVC 5 then you can use Attribute routing and you should then have the possibility to write custom routes next to your Controller Action methods like this..
[Route("License/RequestKey/{apello}/{requestcipher}")]
public string RequestKey(string apello, string requestcipher)
{
return "Yeah";
}

"routes.LowercaseUrls = true;" does not work?

I'm having trouble in setting my routes to lowercase by default. For some reason it does not work. I know I can set authorize and home to lowercase myself, but the Admin part (area) will still be capitalized..
#Html.ActionLink("Hello World", "Authorize", "Home")
outputs to
Hello World
Area route
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.LowercaseUrls = true;
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "OR.Areas.Admin.Controllers" }
);
context.Routes.LowercaseUrls = true;
}
Default route
public static void RegisterRoutes(RouteCollection routes)
{
routes.LowercaseUrls = true;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
routes.MapRoute(
name: "Localization",
url: "{lang}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "OR.Controllers" }
);
routes.LowercaseUrls = true;
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "OR.Controllers" }
);
routes.LowercaseUrls = true;
}
Admin Area configs I tried
// admin/Home/Authorize
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.LowercaseUrls = true;
context.MapRoute(
"Admin_default",
"{area}/{controller}/{action}/{id}",
new { area = "admin", controller = "home", action = "Index", id = UrlParameter.Optional },
new string[] { "ORMebeles.Areas.Admin.Controllers" }
);
context.Routes.LowercaseUrls = true;
}
// admin/Home/Authorize
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.LowercaseUrls = true;
context.MapRoute(
"Admin_default",
"admin/{controller}/{action}/{id}",
new { controller = "home", action = "Index", id = UrlParameter.Optional },
new string[] { "ORMebeles.Areas.Admin.Controllers" }
);
context.Routes.LowercaseUrls = true;
}
Edit
As it seems this is bug with MVC4 - when you set context.Routes.LowercaseUrls = true; and you have Area/Areas context.Routes.LowercaseUrls = true; won't take any effect, where should we report it or how can we get it fixed?
This is bug related to MVC4 and will be fixed in MVC5 release. Routes.LowercaseUrls does not affect areas. More info here.
Meanwhile you can use LowercaseRoutesMVC or
LowercaseRoutesMVC4 if you need WebApi support.
I tried Several attempts to get this particular boolean flag to work with an MVC3 project with no luck. The ONLY way I could get it to work was to create an MVC4 application project and set the flag in the RouteConfig.cs file in the app start. The really bad part is it lowercased the urls across the site automatically for me until I added an area, then it broke everywhere. Once I excluded the newly added area from the project and reran, the urls were lowercased again.
Something is wonkey with the use of that flag. I would recommend downloading the nuget package for lowercasing urls. It seems as if they haven't quite worked out the kinks in this part of the new framework.
Sorry I couldn't be of more help.
UPDATE: IN AN MVC4 application
Create a new blank MVC4 application and add an Area Called Test, with a Test.cshtml View and a TestController.cs controller.
So I figured out something... though I am not sure if it's a reasonable solution. After playing with the route registration routines, having the areas in the project doesn't break the lowercase functionality.
namespace MvcApplication1.Areas.Test
{
public class TestAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Test";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
//This line breaks the functionality in the area registration.
context.MapRoute(
"Test_default", // Route name
"Test/{controller}/{action}/{id}", // URL with parameters
new { controller = "Test", action = "Index", id = "" }, // Parameter defaults
new string[] { "MvcApplication1.Areas.Test.Controllers" } //namespace
);
}
}
}
A workaround:
Comment out the lines
//context.Routes.LowercaseUrls = true;
//context.MapRoute(
// "Test_default", // Route name
// "Test/{controller}/{action}/{id}", // URL with parameters
// new { controller = "Test", action = "Index", id = "" }, // Parameter defaults
// new string[] { "MvcApplication1.Areas.Test.Controllers" } //namespace
// );
In RouteConfig.cs
namespace MvcApplication1
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Test_default", // Route name
"Test/{controller}/{action}/{id}", // URL with parameters
new { controller = "Test", action = "Index", id = "" }, // Parameter defaults
new string[] { "MvcApplication1.Areas.Test.Controllers" } //namespace
);
}
}
}
In The Area Controller Action Method
public ActionResult Index()
{
// Key if statement to make sure the area maps correctly
if (!this.ControllerContext.RouteData.DataTokens.ContainsKey("area"))
{
this.ControllerContext.RouteData.DataTokens.Add("area", "Test");
}
return View("Test");
}
Resulting HTML for the links in the main page of the project
<ul id="menu">
<li>Home</li>
<li>About</li>
<li>Contact</li>
<li>Test</li>
</ul>
Notice however the query string variables are not lowercased and it is not an seo friendly url. However it does find the view. This is as close as I've been able to come using that flag and having the urls go to lowercase.
As I known, LowercaseUrls = true is only available in .NET4.5, maybe you can just write some extensions for lowercase urls. you can refer to making URL lowercase. Any easy or builtin way for detail info.

How can I add a route in controller

I'd like to map a new route after I commit a new object to db. So for example if i enter object with name "Test" I would like to have a new route immediately, to resolve "Test.aspx".
I tried
System.Web.Routing.RouteTable.Routes.MapRoute(obj.NameUrl, obj.NameUrl + extension, new { controller = "per", action = "Index", name = obj.NameUrl });
in controller but it does not work (no error, just probably not right time in life cycle?). Same code works in Application_Start()
You should avoid registering routes dynamically. The following static route in your Application_Start should be able to handle your scenario of having dynamic route parameters:
routes.MapRoute(
"page",
"{name}.aspx",
new { controller = "per", action = "index" },
new { name = #"[a-z0-9]+" }
);
and if the extension has to be dynamic as well:
routes.MapRoute(
"page",
"{name}.{extension}",
new { controller = "per", action = "index" },
new { name = #"[a-z0-9]+", extension = #"[a-z]{3,4}" }
);
and then you could have the Index action to handle requests to this route:
public class PerController: Controller
{
public ActionResult Index(string name, string extension)
{
...
}
}
and if you want to generate a link to this action:
#Html.RouteLink("go to foo", "page", new { name = "foo", extension = "aspx" })

Language in URL, Routing and Areas

I've learned so far how to set up a correct routing if I would like to have the language within the URL, e.g. .../en/MyController/MyMethod. With the following routing this works great so far:
routes.MapRoute("Default with language", "{lang}/{controller}/{action}/{id}",
new
{
controller = "Report",
action = "Index",
id = UrlParameter.Optional,
}, new { lang = "de|en" });
// Standard-Routing
routes.MapRoute("Default", "{controller}/{action}/{id}", new
{
controller = "Report",
action = "Index",
id = UrlParameter.Optional,
lang = "de",
});
Now I Inserted a new area Cms, and I call AreaRegistration.RegisterAllAreas(); in Application_Start().
As soon as I call a controller within this area, I miss the language-key:
MvcHandler handler = Context.Handler as MvcHandler;
if (handler == null)
return;
string lang = handler.RequestContext.RouteData.Values["lang"] as string;
How could I make the above routing work with areas?
Thx for any tipps, sl3dg3
Check out the generated class that derives from AreaRegistration, named [AreaName]AreaRegistration.
It contains a route registration as well, this is the default:
context.MapRoute(
"AreaName_default",
"AreaName/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
The following routing works now in my case (the area is called Cms):
using System.Web.Mvc;
namespace MyProject.Areas.Cms
{
public class CmsAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Cms";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute("Cms_default_with_language", "Cms/{lang}/{controller}/{action}/{id}", new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional,
lang = "de",
}, new { lang = "de|en" });
context.MapRoute(
"Cms_default",
"Cms/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional, lang = "de" }
);
}
}
}
The only thing I'm not quite happy about: now I have more or less duplicate code in Global.asax and in this class. Is there a way to avoid these duplicate mappings?

Categories

Resources