ASP.net MVC routing parameters - c#

I have route which looks like this:
http://localhost:1936/user/someOne/##feed%233dbfc1b3-44b4-42b4-9305-4822ac151527
My routes are configured like this:
routes.MapRoute("Profile", "user/{userName}/{treningId}",
new { controller = "Profile", action = "Index", userName = UrlParameter.Optional, treningId = UrlParameter.Optional }
);
And Action in my controller like this:
public ActionResult Index(string username, string treningId)
{
//more code here
}
But I can't get this part: ##feed%233dbfc1b3-44b4-42b4-9305-4822ac151527, when I debugged code, I saw treningId was null.
My question is how can I get part of URL after ## ?

This is not possible as hashes are not included in the request; they are reserved for client-side use in URLs (typically to mark a specific location in/portion of a document).
You have two options. Either encode the hash character as %23:
%23%23feed%233dbfc1b3-44b4-42b4-9305-4822ac151527
Or use a different character/route.

You can't.
The browser doesn't send the stuff after the first # to the server at all (you can verify this by using the debugger in your browser to inspect the traffic between your browser and the server). That is because the # (hash) character carries special meaning in HTTP.
Why don't you just
use regular query parameters (so, something like http://localhost:1936/user/someOne?myparameter=feed%233dbfc1b3-44b4-42b4-9305-4822ac151527), or, alternatively,
choose another delimiter than "##".

The way you have your URL written, the browser would not submit any portion of the fragment identifier - the hash part of the URL. As such, your MVC app will never receive the value and thus will always have it as null.
To make that value available to MVC, escape the # to avoid them marking beginning of fragment portion of URL.

Related

Is HttpCookie path case sensitive?

This is for a C# ASP.NET MVC 5 web application, using .NET Framework 4.6.1.
System.Web.HttpCookie has a Path property which is, "the virtual path to transmit with the cookie." The default value of "/" means the server root.
Is that path case sensitive? I think that it should be case insenstive, since the path parts of a URL are case insenstive. [Update: this assumption on my part is wrong. Vide answer below.] However, my experience is proving otherwise.
Fetch cookie
The web app has a FooBarController, which has a index() method where I fetch the value of a cookie and then render a view.
using System.Web;
using System.Web.Mvc;
public class FooBarController : Controller
{
public ActionResult index( )
{
HttpCookie cookie = HttpContext.Request.Cookies.Get( "page_length" );
if ( cookie != null )
{
// Do something with the page length provided by cookie.Value.
...
}
...
return View( somedata );
}
...
The following is the routing to the methods of that controller. Note that it does not matter if "foobar" or "FooBar" is used in the routing. I have tried both.
public class RouteConfig
{
public static void RegisterRoutes( RouteCollection routes )
{
routes.MapRoute( "", "foobar/index", defaults: new { controller = "foobar", action = "index" } );
routes.MapRoute( "", "foobar/load", defaults: new { controller = "foobar", action = "load", HttpVerbs = HttpVerbs.Post } );
}
}
Store cookie
In a different method of that controller, I store a value in a cookie. Take it as given that the following load() method is correctly called during the lifetime of the web app, and the cookie is correctly stored in the client side browser.
public JsonResult load( TableData data )
{
HttpCookie cookie = new HttpCookie( "page_length" );
cookie.Value = data.PageLength.ToString();
// *** This is the important part. ***
cookie.Path = "/FooBar";
cookie.Expires = DateTime.Now.AddDays( 30 );
HttpContext.Response.Cookies.Add( cookie );
...
}
Test 1: Case sensitive path: Works
As mentioned previously, the cookie is successfully stored in the client side browser.
I launch the web app, and navigate to the following URL. Observe that the URL contains the mixed case "FooBar", even though it is not necessary to reach the desired page.
/localhost:12345/FooBar
In FooBarController.index(), I can successfully fetch the cookie and use its value.
Test 2: Case insensitive path: Does not work
The cookie is still successfully stored in the client side browser.
I launch the web app, but navigate to the following URL. Observe that the URL contains a lowercase "foobar", which is sufficient to reach the desired page.
/localhost:12345/foobar
In FooBarController.index(), I cannot fetch the cookie. The cookie is hidden from that method, presumably because it is associated only with the mixed case path of "/FooBar".
Test 3: Mitigation
Store the cookie with a lowercase path of "/foobar".
public JsonResult load( TableData data )
{
HttpCookie cookie = new HttpCookie( "page_length" );
cookie.Value = data.PageLength.ToString();
// *** This is the important part. ***
cookie.Path = "/foobar";
...
I launch the web app, and navigate to the following URL, same as Test 2.
/localhost:12345/foobar
In FooBarController.index(), I can successfully fetch the cookie and use its value.
Why?
Why is it necessary for the path of the cookie to be the same case as the path part of the URL for the page being navigated?
Yes, the path of an HttpCookie is case sensitive.
As pointed out in the comments, my assumption that the path part of URL is case insensitive is wrong. Therefore, it is reasonable to expect that the path stored in cookie should be case sensitive.
The routing configuration for a C# ASP.NET web application is more forgiving. When calling RouteCollection.MapRoute(), the URL pattern provided can be in a different case than that of the actual URL used for navigation. The controller and action names provided to MapRoute() can also be in a different case than that of the actual controller class name and method name. Thus, I was misled.
https://www.ibm.com/docs/en/cics-ts/5.2?topic=concepts-components-url says:
The scheme and host components of a URL are not defined as case-sensitive, but the path and query string are case-sensitive. Typically, the whole URL is specified in lowercase.
Most other URL documentation I found may specifically say that other parts, such as the scheme and host, are case insensitive. And since it does not say anything about the case of the path part, I am supposed to infer that it is case sensitive.

API controller route attribute issue

I have an issue with an api controller attribute routing. The API receives a get request containing a scanned barcode to the route: "api/[controller]/{userId}/{barcodeId}". Where the barcode is like: 0015A765248.
But we now have some new barcodes coming in this format: BBRT058/639.
I've found out through debugging that the extra / in the new barcode is causing the issue as the API controller is now not being accessed.
As I think it is trying to reach controller: "api/[controller]/{userId}/{barcodeId}/{barcodeID}"
Which doesn't exist. Is it possible to modify the API controller route attribute to accept the barcodeId containing the additional /?
Actually modifying the new barcode style in any way code wise is not an option I am told.
I have considered creating a new controller to accept the new barcode: "api/[controller]/{userId}/{barcodeIdpart1}/{barcodeIDpart2}" for example.
But i'm not sure if creating a new controller that is a duplicate of the current one, with the only difference being the routing is a good idea. Also both barcode formats are likely still going to be used in the future.
I don't believe you need to make any changes to your code/controller as such and that it seems to be correct. However, I would recommend you update the client to correctly URL encode the barcode before making the outbound call to your controller. This will ensure that the controller correctly parses the barcode and then processes it further. Additionally, this will also ensure that any other special non ASCII characters are correctly handled in the future.
This solution is not really changing the barcode format but is encoding it just before making the outbound HTTP call to correctly transmit it over the wire. Alternate solutions by hacking the controller will usually result in a non standard brittle solution and is not recommended.
Client calls URL
Current: api/controllername/userId/BBRT058/639
Proposed: api/controllername/userId/BBRT058%2F639
For further reading:
https://en.wikipedia.org/wiki/Percent-encoding
https://www.tutorialspoint.com/html/html_url_encoding.htm
https://www.w3schools.com/tags/ref_urlencode.ASP
I would try to replace "/" with "!" or "!!" for example (you can select any sign or combination you like) - BBRT058!!639 before sending to the controller and replace back with "/" inside of the action.
barcodeId = barcodeId.Replace("!!", "/");
if you can't modify the barcode then try this
Route[("api/[controller]/{userId}/{barcodeId}/{barcodeIdpart2?})"]
public IActionResult GetBarcode(int userId, string barcodeId, string barcodeIdpart2 = null)
{
if (!string.IsNullOrEmpty(barcodeIdpart2)) barcodeId=barcodeId + "/" + barcodeIdpart2;
....
}
It will work for both styles

Routing: how do I redirect to a url when a parameter is missing?

I have routes like this:
example.com/{category}/articles.
There isn't a route for
example.com/{category}
example.com/
I want to redirect all that traffic to
example.com/{category}/articles with a default value.
I've read that I can use default values with a RouteValueDictionary:
routes.MapPageRoute("ArticlesAll", "{category}/articles/", "~/ArticlesPage.aspx", false, new RouteValueDictionary { { "category", "cats" } });
But that doesn't do any redirecting.
Would I need another route to forward to the one above or is there a more efficient way of doing this?
What you are asking is a bit unclear, but let me offer what I usually do.
If you are trying to make it so that two similar links can be written, for example www.example.com/read/penguin/article/ and www.example.com/read/panda/article. I would just write the whole string / URL with a variable in the middle:
private string destination = "penguin";
private string url = "www.example.com/read/" + destination + "/article/";
void Test () {
destination = "lion";
text.URL = url;
}
Sorry, I may have made gramatical mistakes in my code, but I hope you get the point. I may have completely misunderstood you though, so clarification would be appreciated.
You can setup these two routes:
routes.MapPageRoute("ArticlesAll", "{category}/articles/{*value}", "~/ArticlesPage.aspx");
routes.MapPageRoute("CatchAll", "{*value}", "~/ArticlesPage.aspx");
to catch anything and redirect to your desired page. I just assumed where you want to lead your users, change the aspx part as needed. Keep in mind that you can use {value} to access the whatever the user wrote.
For example if they wanted to navigate to dogs and you wanted to redirect to dogs/articles/ArticlesPage.aspx, you should use:
routes.MapPageRoute("CatchAll", "{*value}", "~/{value}/articles/ArticlesPage.aspx");
EDIT
If you want to actually redirect to the new URL and not just serve up the right page, you can use the CatchAll route to redirect to a page (say Redirect.aspx) that's sole purpose is to parse data from the RouteData object, construct the new URL and Redirect.

Return url functionality

I'm using ASP.NET Core MVC with C#.
I have a functionality on my Login controller to take a parameter called ReturnUrl. The Idea is, that if this is set when the user logs in then I can redirect them back to the page. Straight forward enough.
The code looks like this:
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string returnUrl = null)
{
...
}
However, the login functionality is handled by MVC and the main content is using Angular (version 1 not 2). Routes in my app are (for example) http://localhost/#/location1.
If I hit my MVC login page with (again, just an example) http://localhost/Login/?returnUrl=http://localhost/#/location1, then all I get in my returnUrl parameter is http://localhost/. It drops anything from the # symbol onwards.
Any ideas how to stop the code automatically escaping at the # symbol and taking the full url as the returnUrl object?
if you notice in your browser's debug tools, the browser would not be sending this # and any text after it to the server, so the server wouldn't know about its existence at all.
So instead of something like below,
http://localhost/Login/?returnUrl=http://localhost/#location1
do this (note the encoding of # into %23 in the url)
http://localhost/Login/?returnUrl=http://localhost/%23location1
I have 2 ideas that might work for you:
try escaping the return url in the client vefore sending the request.
instead of string change the return url type to Uri object
[HttpGet]
[AllowAnonymous]
public IActionResult Login(Uri returnUrl = null)
{
...
}

Redirect any incoming URLs with diacritics to equivalent with no diacritics

How does one implement URL redirecting for URLs with accents? I'd like all potential URL requests with accents to be rewritten without the accent. This is for client names on a French language site. Users should be typing the name without the accent, but should they not do so then I'd like them to land on the correct page either way.
Example
User enters: www.mysite.fr/client/andré ==> user is redirected towww.mysite.fr/client/andre
The resource identifier (clientName) in the database is stored without the accent. Here's my RouteConfig.cs :
routes.MapRoute(
name: "ClientDetails",
url: "client/{clientName}",
defaults: new { controller = "Client", action = "ClientDetails" }
I understand that there are various methods for removing accents on a string. And yes, this would allow me to remove accents from the received URL parameter within the Controller method. However, I would like to be consistent, so that users always see URLs displayed without accents. Therefore, how do I implement redirecting URLs throughout my web application? Is there an easy way to do this in RouteConfig.cs or Web.config ?
I think you mean Redirect and not Rewrite given that Rewrite would mean that the URL stays the same, and you display the intended content (which I think is what you don't want).
The strategy that I think you want, can be implemented by creating a custom route constraint that matches everything that has an any special chars on it.
Make this custom route to be the first thing to be evaluated in your route table, and map this custom route to a "RedirectController" (that you will create) that takes care of removing the special chars from the URL and redirecting the user to a URL with no special chars
At the beginning of every request, you can make this check to perform a redirect. In your Global.asax.cs file, include the following...
protected void Application_BeginRequest()
{
var originalUri = HttpContext.Current.Request.Url;
var finalUri = new UriBuilder(originalUri);
finalUri.Path = RemoveAccents(
originalUri.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped)
);
// Check if the URL has changed
if (!originalUri.Equals(finalUri.Uri))
{
HttpContext.Current.Response.Redirect(finalUri.Uri.ToString(), true);
HttpContext.Current.Response.End();
}
}
You might also want to add another line for finalUri.Query and UriComponents.Query. For RemoveAccents, I tried the following code but you can use what you'd like:
public string RemoveAccents(string input)
{
return Encoding.UTF8.GetString(Encoding.GetEncoding(1251).GetBytes(input));
}

Categories