MVC4 -> Mapping one path to another ( not a controller ) - c#

OK, let me ask this a different way. We have a web app. We have paid and trial subscribers. Each of them current has a folder named after their user id, and that's where their code lives. The code gets their user id from the URL, so we can't change the URL. I want to consolidate the location of our code, so that people go to the same URL as they are today ( sitename/username ), but it will return the application code ( which is a folder structure ) from a single location. I've tried everything I can think of, but Server.Transfer can't transfer to a folder, transfering to the index.html does not work, Server.TransferLocation and ( of course ) Response.Redirect change the URL on the client side.
I have tried doing this in a RouteHandler, and in a Controller, it makes no difference, I cannot find a way to redirect the user transparently to a central code base. Ideally, I'd do it in code, so I can validate what sort of user they are and redirect them accordingly.
After many iterations, here is the core code, right now:
public class FarmHttpHandler : IRouteHandler
{
enum UserType { Invalid = -1, Standard = 1, Tester = 2, Developer = 3}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// Note - this has to be a valid URL, or it will loop forever, as our HTTP handler is at the base level.
// We need to add a 'bad request' page, or it could default to the demo ( this would be a support nightmare though, if someone mistyped their URL,
// they would think it was fine, at first.
string redirect = "/Home/Error";
var routeValues = requestContext.RouteData.Values;
if (routeValues.ContainsKey("farmName"))
{
string farmName = routeValues["farmName"].ToString();
string baseUrl = "/";// HttpContext.Current.Request.Url.AbsoluteUri.Replace(HttpContext.Current.Request.Url.AbsolutePath, "/");
switch(GetUserType(farmName))
{
case UserType.Invalid:
break;
case UserType.Developer:
redirect = baseUrl + Settings.BetaAppPath;
break;
case UserType.Standard:
redirect = baseUrl + Settings.FullAppPath;
break;
case UserType.Tester:
redirect = baseUrl + Settings.TesterAppPath;
break;
}
}
HttpContext.Current.Server.TransferRequest(redirect);
requestContext.HttpContext.RewritePath(redirect);
return requestContext.HttpContext.Handler;
}
This gets called, but the rewritepath doesn't do anything ( the error says no IHttpHandler was returned ) and the TransferRequest works, but the URL changes in the browser. The code also uses relative paths for images, etc, and these do not load ( not sure if I can actually fix this ).

The answer is
return BuildManager.CreateInstanceFromVirtualPath(URl, typeof(Page)) as Page;
That's how you return a redirect from a routehandler. Sadly, it won't work for me, the browser can't work out to redirect the requests within our web app, and it doesn't make requests to a full URL, which means I can't detect them to remap them.

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.

How to manipulate a url to access a parent directory

I have an asp.net web application that is being hosted on an internal network. In my testing environment of course it gets hosted out on localhost:01010/Views/page.aspx. now whenever I take it live the Url changes to server_name/folder 1/folder 2/views/page.aspx. what I am trying to do is get a new page to open up as server_name/folder 1/folder 2/Uploaded_Images/randomimage.png. Now I Can get the url, but as soon as I do a single ".Remove(url.lastindexof("/")+1)" it returns "server_name/folder 1/folder 2/Views". The I perform my second ".Remove(url.lastindexof("/")+1)"
and the it only returns "server_name/". I am ripping my hair out at this one and am hoping somewhere in the world a .net developer already has this built in. Appreciate all the help.
Also just to specify this is webforms and not mvc. also there is no ajax or page manipulation going on except for a response.write to open the new page.
You don't need the +1, this works:
var url = "server_name/folder 1/folder 2/views/page.aspx";
url = url.Remove(url.LastIndexOf("/"));
url = url.Remove(url.LastIndexOf("/"));
Or you could do it like this:
var parts = url.Split('/');
var newPath = string.Join("/", parts.Take(3));
I assume you are talking about URL's used as links to parts of your site and not physical paths on the file system.
In most cases, you should be able to use methods that construct paths on the fly. For example, in any of your .aspx files (or .aspx.cs files), you can use the ResolveUrl method, like this:
Some link
If there are any places where you need the full URL including the domain (like for email notifications or something like that) then what I have done is keep a static variable accessible to my whole application that gets set the first time Application_BeginRequest runs:
void Application_BeginRequest(object sender, EventArgs e) {
if (SiteRoot == null) {
SiteRoot = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) +
(VirtualPathUtility.ToAbsolute("~") == "/" ? "" : VirtualPathUtility.ToAbsolute("~"));
}
}
That will pull the full URL from the Request details (the URL that the user used to access your site), without any trailing slash.

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.

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));
}

How do I redirect to my parent action in MVC site?

I have been looking at several pages on here already such as:
How do I redirect to the previous action in ASP.NET MVC?
How can I redirect my action to the root of the web site?
Along with several hours of searching google.
No where seems to have an answer to my problem and I am sure it should be possible within MVC somehow hence the reason I am now here to ask the question.
So the problem I am facing is that I want to allow the user to change the language of the page by choosing a new language from a drop down menu which is in its own partial view hence the problem, I need to redirect to the parent action and not the child. This all works fine as long as i send the user back to the root of the site. Using the following code:
[HttpPost]
public ActionResult RegionSelect(RegionSelectionModel model)
{
var currentUser = Session.GetCurrentUser();
var currentDbUser = Session.GetUserEntity(_dataAccessLayer);
if (!ModelState.IsValid)
{
model.AvailableRegions = CacheHelpers.GetAvailableRegions<RegionView>(_dataAccessLayer, _cache).ToList();
return PartialView("_RegionSelect", model);
}
var selectedRegion = UsersControllerHelpers.SetSelectedRegion(model, _dataAccessLayer, _cache, _website.Client);
var uri = model.OriginalUrl;
var routeInfo = new RouteHelpers(uri, HttpContext.Request.ApplicationPath);
// Route Data
var routeData = routeInfo.RouteData;
routeData.Values.Remove("language");
var defaultClientLanguageCode = _website.Client.LanguagesSupported.FirstOrDefault().Code;
if (currentDbUser.Language.CountryCode != selectedRegion.PrimaryLanguage.CountryCode)
{
//TODO: Decide where to redirect or whether to refresh the whole page...
if ((defaultClientLanguageCode == selectedRegion.PrimaryLanguage.CountryCode) || (model.SelectedRegionId == 0))
{
UsersControllerHelpers.UpdateUsersRegions(currentUser, selectedRegion, _website.Client, _cache, _dataAccessLayer,
Session);
return RedirectToRoute(routeData.Values);
}
routeData.Values.Add("language",selectedRegion.PrimaryLanguage.CountryCode);
return RedirectToRoute(routeData.Values);
}
return RedirectToRoute(routeData.Values);
}
Two of my return statements return to the root page and one returns to the root but with a language so it would be "http://mysite/en-En/" but what if the user is on a page other than the root site? I want to somehow redirect them back to this same action but with the correct language string at the start.
How can i do this?
I have thought of several "hacky" ways of doing this, such as splitting the URL and swapping the language codes over. But ideally I am looking to do this as clean as possible.
Can anyone give me any idea's? Or is it just not possible?
It seems like it should be really simple but apparently not.
Thanks in advance for any help that you can provide.
EDITED
Added new code that is using code from suggested answer below.
I am now having two new problems.
I am getting this error message, if there are any things in the URL such as ?page=1:
A potentially dangerous Request.Path value was detected from the client (?)
If i try and remove the language completely using .Remove(). It removes it fine but when i try and redirect to the page in the default language it adds language?=language to the end of the URI.
Any ideas how i can resolve these two issues?
This option is definitely my answer. Leave me a comment if you need me to drop some code, and I can do that, but the examples on the linked website should get you started.
Use this method to change Request.UrlReferrer into Route data, then merge your language into that, then do a RedirectToRoute with the modified Route data.
Just use RouteData.Values.Add, RouteData.Values.Remove, and RouteData.values["whatever"], then pass that modified RouteData.Values object to RedirectToRoute()

Categories