ASP.NET 4 RouteDictionary does not take the default values - c#

I have this route in ASP.NET 4
routes.MapPageRoute("Professions", // Route Name
"authors/profession/{Profession}/{letter}/page{pager}", // Url and Parameters
"~/Authors/Profession/Default.aspx", true,
new System.Web.Routing.RouteValueDictionary { { "letter", "a" }, { "pager", "1" } }); //
It works if I access the page like this
http://www.mysite.com/authors/profession/actor/a/page1
but this does not work http://www.mysite.com/authors/profession/actor (it should add automatically the letter 'a' and page 1, what I am doing wrong?

The problem you got here is the text "page" in the route which as after your {letter} token (which you are not providing in the example url).
For the routing engine to match that route, you would need both a "letter" and the text 'page' somewhere in the url. So your example /authors/profession/actor would not match whereas /authors/profession/actor/a/page would match and use the default value of 1 for the {pager} token.
I think the best way to approach this would be the following route setting:
routes.MapPageRoute("Professions", // Route Name
"authors/profession/{profession}/{letter}/{pager}", // Url and Parameters
"~/Authors/Profession/Default.aspx", true,
new System.Web.Routing.RouteValueDictionary { { "letter", "a" }, { "pager", "1" } });
Essentially, just take 'page' text out of the route so that when you have /authors/profession/actor it would go straight to the letter "a" and page "1". And a full url would be something like /authors/profession/actor/d/34.
Does that make sense? Your routing is perfectly valid, but you would always have to provide something for the {letter} token and have the text 'page' in there too.

Related

Is it possible to include the application name in the asp.net core routes?

I have a simple phonebook app that allows the user to search for an employee via a dropdown list of departments or by name via text input. The way I'm handling the search is simply changing the action attribute of the html form with javascript based on whatever they typed or whichever option they selected in the dropdown:
function selectedOption(option) {
var selValue = option.value;
document.getElementById("search-form").action = "/home/index/" + selValue;
}
This works on localhost but when I host it on IIS:
machineName/appName
becomes
machineName/home/index/selValue
cutting off the app name and returning a 404 error
The only way I've been able to get around this is with some hardcoding to check whether "home/index" exists in the path already...
function selectedOption(option) {
var selValue = option.value;
if (window.location.href.includes("home")) {
var searchDest = selValue
} else {
var searchDest = window.location.href + "/home/index/" + selValue
}
document.getElementById("search-form").action = searchDest;
}
This works but it is not very clean or conventional. Is there some way I can have the app name configured in Startup.cs? I saw the same issue in another question here but I'm getting syntax errors when I try to add another MapRoute. This is all that I currently have:
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
Any idea how to get around this?
as mentioned above by #Desmond Chin, for cshtml you can use HTML helpers. I think the best one for that case is to use #Url.Action(actionName, controllerName) instead #Url.Content() - which is used for js, css, img, etc..
You can use this code bellow to achieve this:
function selectedOption(option) {
var selValue = option.value;
document.getElementById("search-form").action = '#Url.Action("Index", "Home")?<<VARIABLENAMEHERE>>'+selValue;
}
The Html Helper will take care of your url, virtual paths and etc

.NetCore 2.x on using {*catchAll} routing URL.Action always returns null

I am using this route to map all routes which were not found:
routes.MapRoute(
name: "NotFound",
template: "{*url}",
defaults: new { controller = "Home", action = "NotFound" }
);
The problem I encountered is that #Url.Action() always returns null on this route.
Could someone explain why is this happening and what could be alternatives to this?
you have to add the below code before the app.UseMvc(...[Routing] ...), make sure you use it in the right order because the order really matters, the pipeline in asp.net core is in reverse order meaning that if you add A before B then B will be called before A, you can read more here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1#order
app.UseStatusCodePagesWithReExecute("/error/{0}");
and in the controllers consider an ErrorController which contains different error codes here we just consider 404 and 500 errors in ErrorController, we must have the corresponding views for each error code (404, 500, Unknown)
public class ErrorController : ControllerBase
{
[Route("error/{code:int}")]
public ActionResult Error(int code)
{
switch (code)
{
case 404: return View("404");
case 500: return View("500");
default: return View("Unknown");
}
}
}
for more concise description please check Microsoft documentation here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-2.1
I suppose you call #Url.Action() either with route parameters (e.g. controller = "Home", action = "NotFound") or with route name - that makes no difference - to generate a URL to your not found page. Well, you said the URL can be any thing which effects in in-bound routing and it's just fine. But, when you are trying to generate URLs from a route (out-bound routing), the route hasn't any template to generate a URL. In route's perspective, it can be any URL. So, null is any URL too. So, the null will be returned.
After playing around, I found a "cheat" way to do it and it kinda works. If I get NotFound page then I redirect back to the same page if Url.Action() == null
if(this._urlService.Action() == null) //same urlHelper action, only has default Home page values passed into method
{
var query = string.Empty;
if(Request.QueryString.HasValue)
{
query = Request.QueryString.Value;
}
var path = "/Home/NotFound" + query;
path = string.Concat("/", this._userIdentity.CurrentLanguage.ToString().ToLower(), path);
return base.Redirect(path);
}
It could be because I use /{culture}/{Controller}/{Action} as my main route. Creating other test project, where my main route is default /{Controller}/{Action} has no problem at all finding the Url.Action() on NotFound page

URL Routing Not Working in ASP .Net MVC2

I am working ASP.Net MVC2 application.
In that i have used URL Routing
To get URL as
https://localhost/StudentDetail/SortField
I have written below code in Global.asax
routes.MapRoute(
"StudentDetail", // Route name
"StudentDetail/{SortField}", // URL with parameters
new { controller = "UDashboard", action = "UAboutMeStudentDetails",
SortField = "Major" }
);
And In my view link is as below
<a href="/StudentDetail?SortField='Major'" >Students</a>
But it is not working. and my URL is
https://localhost/StudentDetail?SortField='Major'
Can anyone please help me to get the required URL..?
I want URL as
https://localhost/StudentDetail/SortField
Thanks In Advance, Prashant
I think you have an incorrect thought on how routing works. Your route:
routes.MapRoute(
"StudentDetail", // Route name
"StudentDetail/{SortField}", // URL with parameters
new { controller = "UDashboard", action = "UAboutMeStudentDetails",
SortField = "Major" }
);
Will take the SortFeild parameter (Major, Gpa, etc), and replace {SortField} with that value. So, using the following actionlink:
#Html.ActionLink("Student Details", "UAboutMeStudentDetails", new {controller="UDashboard", SortField = "Major})
would produce the following HTML
Student Details
Note that the value of SortField has replaced the {SortField} parameter in your route. You would never get a URL looking like what you are requesting as how would you get the value of SortField to the action?

How to get RouteCollection from a request in ASP.NET MVC 4

This is my global.asax (Registering the route)
routes.MapRoute("NewDatabase",
"Server/{serverId}/Instances/{instanceId}/NewDatabase/",
new { controller = "Server", action = "NewDatabase" }
);
routes.MapRoute(
"Instance",
"Server/{id}/Instances/{instanceId}/Databases",
new { controller = "Server", action = "Databases", id = "id",instanceId="instanceId" }
);
routes.MapRoute(
"Database",
"Server/{id}/Instances",
new { controller = "Server", action = "Instances", id = "id" }
);
If xyz.com/Server/12/Instance/1/NewDatabase will be the reuested URL to server,
Server/{serverId}/Instances/{instanceId}/NewDatabase/ will be the matching pattern.
How can I know which entry is matched for the above request ?
Thanks
RouteTable.Routes.GetRouteData(HttpContextBase)
Install the RouteDebugger nuget package and enabled it in your web.config. This tells you which routes can be hit and why.
<add key="RouteDebugger:Enabled" value="true" />
All you have to do is navigate to your url. RouteDebugger will add route info to the bottom of your page.
I am not sure if this is what you are looking for but you can create a custom route constraint which enables you to prevent a route from being matched unless some custom condition is matched.
You can implement Match method which returns a Boolean value. If you return false, the route associated with the constraint won’t match the browser request. This method has RouteValueDictionary so you can check route values from there.
see this url for more information - http://www.asp.net/mvc/tutorials/controllers-and-routing/creating-a-custom-route-constraint-cs

asp.net mvc localization

I'm trying to implement localization with routes
I have the following:
routes.MapRoute( "DefaultLocalized",
"{lang}/{controller}/{action}/{id}",
new { controller = "Home",
action = "Index",
id = "",
lang = "en" }
);
routes.MapRoute( "Default",
"{controller}/{action}/{id}",
new { controller = "Home",
action = "Index",
id = "" }
);
When I call my page domain/en/home/index, it works fine but when i call domain/home/index I get error 404: resource cannot be found.
Also when I'm at domain/en/home/index and I click a secured page I get redirected to domain/Account/login how can I be redirected to domain/en/Account/login?
Also when I get an application error how can I be redirected to domain/en/home/error?
The real question is how can I implement localization with language as a route parameter?
The routes will match, by default, left-to-right, so "domain/home/index" will match first to lang=domain, controller=index, action (default to index), id (default to 0/null).
To fix this, I believe you can specify a regex on the MapRoute (matching, for example, languages with exactly 2 characters) - it has changed at some point, though... (sorry, no IDE at the moment, so I can't check exactly).
From memory, it might be:
routes.MapRoute( "DefaultLocalized",
"{lang}/{controller}/{action}/{id}",
new { controller = "Home",
action = "Index",
id = "",},
new { lang = "[a-z]{2}" }
);
Note that you probably aren't going to want every action to take a "string lang", so you should handle the "lang" part of the route either in a base-controller, or an action-filter (in either case, presumably add the info to the ViewData).
I know this is a very old question, but having just had to solve the complete set of related issues, I thought I would share my solution.
Below is a complete solution, including a few extra tricks to allow easy changing of language. It allows for specific cultures, not just specific languages (but only the language part is retained in this example).
Features include:
Fallback to browser locale in determining language
Uses cookies to retain language across visits
Override language with url
Supports changing language via link (e.g. simple menu options)
Step 1: Modify RegisterRoutes in RouteConfig
This new routing includes a constraint (as others also suggest) to ensure the language route does not grab certain standard paths. There is no need for a default language value as that is all handled by the LocalisationAttribute (see step 2).
public static void RegisterRoutes(RouteCollection routes)
{
...
// Special localisation route mapping - expects specific language/culture code as first param
routes.MapRoute(
name: "Localisation",
url: "{lang}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { lang = #"[a-z]{2}|[a-z]{2}-[a-zA-Z]{2}" }
);
// Default routing
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Step 2: Create a Localisation attribute
This will look at controller requests, before they are handled, and change the current culture based on the URL, a cookie, or the default browser culture.
// Based on: http://geekswithblogs.net/shaunxu/archive/2010/05/06/localization-in-asp.net-mvc-ndash-3-days-investigation-1-day.aspx
public class LocalisationAttribute : ActionFilterAttribute
{
public const string LangParam = "lang";
public const string CookieName = "mydomain.CurrentUICulture";
// List of allowed languages in this app (to speed up check)
private const string Cultures = "en-GB en-US de-DE fr-FR es-ES ro-RO ";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Try getting culture from URL first
var culture = (string)filterContext.RouteData.Values[LangParam];
// If not provided, or the culture does not match the list of known cultures, try cookie or browser setting
if (string.IsNullOrEmpty(culture) || !Cultures.Contains(culture))
{
// load the culture info from the cookie
var cookie = filterContext.HttpContext.Request.Cookies[CookieName];
var langHeader = string.Empty;
if (cookie != null)
{
// set the culture by the cookie content
culture = cookie.Value;
}
else
{
// set the culture by the location if not specified - default to English for bots
culture = filterContext.HttpContext.Request.UserLanguages == null ? "en-EN" : filterContext.HttpContext.Request.UserLanguages[0];
}
// set the lang value into route data
filterContext.RouteData.Values[LangParam] = langHeader;
}
// Keep the part up to the "-" as the primary language
var language = culture.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries)[0];
filterContext.RouteData.Values[LangParam] = language;
// Set the language - ignore specific culture for now
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(language);
// save the locale into cookie (full locale)
HttpCookie _cookie = new HttpCookie(CookieName, culture);
_cookie.Expires = DateTime.Now.AddYears(1);
filterContext.HttpContext.Response.SetCookie(_cookie);
// Pass on to normal controller processing
base.OnActionExecuting(filterContext);
}
}
Step 3: Apply localisation to all controllers
e.g.
[Localisation] <<< ADD THIS TO ALL CONTROLLERS (OR A BASE CONTROLLER)
public class AccountController : Controller
{
Step 4: To change language (e.g. from a menu)
This is where it got a little tricky and required some workarounds.
Add a ChangeLanguage method to your account controller. This will strip out any existing language code from the "previous path" to allow the new language to take effect.
// Regex to find only the language code part of the URL - language (aa) or locale (aa-AA) syntax
static readonly Regex removeLanguage = new Regex(#"/[a-z]{2}/|/[a-z]{2}-[a-zA-Z]{2}/", RegexOptions.Compiled);
[AllowAnonymous]
public ActionResult ChangeLanguage(string id)
{
if (!string.IsNullOrEmpty(id))
{
// Decode the return URL and remove any language selector from it
id = Server.UrlDecode(id);
id = removeLanguage.Replace(id, #"/");
return Redirect(id);
}
return Redirect(#"/");
}
Step 5: Add language menu links
The menu options consist of a link with the new language specified as a route parameter.
e.g. (Razor example)
<li>#Html.ActionLink("English", "ChangeLanguage", "Account", new { lang = "en", id = HttpUtility.UrlEncode(Request.RawUrl) }, null)</li>
<li>#Html.ActionLink("Spanish", "ChangeLanguage", "Account", new { lang = "es", id = HttpUtility.UrlEncode(Request.RawUrl) }, null)</li>
The return URl is the current page, encoded so that it can become the id parameter of the URL. This means that you need to enable certain escape sequences that are otherwise refused by Razor as a potential security violation.
Note: for non-razor setups you basically want an anchor that has the new language, and the current page relative URL, in a path like:
http://website.com/{language}/account/changelanguage/{existingURL}
where {language} is the new culture code and {existingURL} is a URLencoded version of the current relative page address (so that we will return to the same page, with new language selected).
Step 6: Enable certain "unsafe" characters in URLs
The required encoding of the return URL means that you will need to enable certain escape characters, in the web.config, or the existing URL parameter will cause an error.
In your web.config, find the httpRuntime tag (or add it) in <system.web> and add the following to it (basically remove the % that is in the standard version of this attribute):
requestPathInvalidCharacters="<,>,&,:,\,?"
In your web.config, find the <system.webserver> section and add the following inside it:
<security>
<requestFiltering allowDoubleEscaping="true"/>
</security>
Add a contraint as new {lang = "[a-z]{2}"}.
Additionally, drop the default lang = "en". If you don't, the routing will grab the language rule when you are browsing it without it. So if you are looking at domain and you select About, it would use domain/en/Home/About instead of the more simple domain/Home/About
You could also introduce a constraint even tighter than Marc Gravell and Freddy Rios.
something like "en|de|fr|es". This would mean hardcoding the languages but usually these are few and known.

Categories