Why does URL Routing break when I use parameters? - c#

I have developed an ASP MVC 5 website with the following route:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{language}",
defaults: new
{
controller = "Member",
action = "Index",
language = UrlParameter.Optional
});
}
I only have that one controller, Member, and everything works running on IISExpress on my local dev machine. But when I try to deploy and access the site on my development server, I get 404 error. The URL I'm passing to it is identical to the one I'm using on the development machine, yet it seems like the routing is not working as expected. Here's a sample URL:
http://myserver:8080/Member/GetCertificate/en-US?mn=MjMzOTA3MDc4MDA=&gn=NjcwNzkz
This is the only route registered on my application, and I've tried to register the wildcard script on IIS, as well as editing my web config with the following entries:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="UrlRoutingModule-4.0"/>
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition=""/>
</modules>
</system.webServer>
The development server is running IIS 7.5. I have tried to access the page remotely from my development machine as well as locally from the web server, with no luck. Here's what the action method looks like for reference:
[HttpGet]
public FileResult GetCertificate(string language, string mn, string gn)
{
var member = new Member()
{
MemberNumber = Encoding.Default.GetString(
Convert.FromBase64String(mn)),
GroupNumber = Encoding.Default.GetString(
Convert.FromBase64String(gn)),
Language = language
};
var certificate = this.certificateRepository
.GetCertificateDocument(member);
return this.File(certificate, "application/pdf");
}
I have continued debugging it, and I found that the problem seems to be that it's recognizing the action as the controller, so if I use the following URL, it works:
http://myserver:8080/Member/Member/GetCertificate/en-US?mn=MjMzODU1NjE5MDE=&gn=NzkxMjgz
But I end up repeating the controller name, which makes for a not so readable URL. Any way around this, though? Maybe I missed something?

The problem was that my application within IIS had a name conflict with my controller. The structure in my IIS was as follows:
MemberSite
Member
MemberService
And the controller in my MVC application was also called MemberController. This caused confusion, because I thought I was referencing the controller, when I was actually referencing the application itself. So for readability, I renamed the application in IIS to MemberApp, and the final URL looks like this:
http://myserver:8080/MemberApp/Member/GetCertificate/en-US?mn=MjMzODU1NjE5MDE=&gn=NzkxMjgz

Related

.NET Core 2.0 MVC odd 404 on controller methods only when deployed to IIS

My home controller sucessfully return index.html from
http://localhost/
and
http://localhost/controller/
but if i try to hit
http://localhost/controller/method/
I get a 404 even though that method works fine in IIS express.
Couldn't find anything online with someone having a similar issue where only the methods on a controller didn't work on one particular deployment but the controller itself is fine.
Things I've Tried that were common among a lot of .Net Core 2.0 issues with IIS Deployments:
Make sure windows authentication is on in project settings and in IIS (I've toggled it on and off to no avail on both I don't have user auth on my web app so I don't think this matters for me).
Switched my application pool to use No managed code for CLR version
Change application pool ID to be LocalSystem
Change permissions on my publish output folder to include %hostname%\IIS_IUSRS
Pretty sure I've also tried a lot of other basic troubleshooting that sometimes fixes issues. I.E. removing and readding app. Turning things on and off again to no avail.
Any suggestions how to troubleshoot this would be very welcome.
I also want to note it was working yesterday and can't remember changing anything other than the publishing output to use Debug instead of Release which of course by now I've changed back to Release but still no luck.
Here is some code
public class MyController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public void Store([FromBody]MyObject obj)
{
Console.WriteLine(Request.Body);
//Some code
}
[HttpGet]
public void Check(string objectUID, string idfv)
{
Console.WriteLine($"ObjectUID: {objectUID}");
Console.WriteLine($"IDFV: {idfv}");
//some other code
}
[HttpGet]
public MyObject Retrieve(string objectUID)
{
Console.Writeline($"ObjectUID: {objectUID}");
//Some Code
}
}
This is my routing.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=MyController}/{action=Index}/{id?}");
});
If you‘re not sure why it doesn’t work, try Attribute-Routing, explained in the Docs
Then you could try it this way:
[Route("[controller]/[action]")]
public class MyController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public void Store([FromBody]MyObject obj)
{
Console.WriteLine(Request.Body);
//Some code
}
// If you have a parameter from the uri or query-string, you can add it to the Template this way
[HttpGet("{objectUID} ")]
public void Check(string objectUID, string idfv)
{
Console.WriteLine($"ObjectUID: {objectUID}");
Console.WriteLine($"IDFV: {idfv}");
//some other code
}
// Or optional parameter like this
[HttpGet ("{objectUID?} ")]
public MyObject Retrieve(string objectUID)
{
Console.Writeline($"ObjectUID: {objectUID}");
//Some Code
}
}
Since you are getting 404 error, I suspect the aspnet core handler is missing from your website. Assuming that you have .NET Core Hosting Bundle installed,
ensure you have the following handler added in the web.config file. If web.config file is missing add a new web.config. Also, add a logs folder at the website root folder for logging.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- To customize the asp.net core module uncomment and edit the following section.
For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
<system.webServer>
<handlers>
<remove name="aspNetCore"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
</system.webServer>
</configuration>
I got ASP.NET Core 2 app running on my machine following these steps:
App Pool setting run under AppPoolIdentity with No Managed runtime configuration
Publish your ASP.NET Core 2 website to a folder. By default it publishes to bin\Release\PublishOutput
Point your IIS website to published folder
I was having a similar issue with getting a 404 when trying to access my site's Account controller. All of the other Razor pages where working correctly. If I change the inheritInChildApplications="false" to true in the web.config the controller starts working.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="true">
...
</configuration>

How to handle a virtual path created by proxy server in MVC route?

I am fairly new to MVC. Recently I developed a site and hosted it behind a proxy server. I access my site using internalhostname/site/{controller}/{action}/{id} for test, which works fine.
However, when my users are connecting the site, they would use an external url like this: externalhostname/apps/site/{controller}/{action}/{id}. Now when my views attempt to initiate a call to the controller according to the default route config, the URLs being generated become externalhostname/site/{controller}/{action}/{id}, notice "/apps" is gone.
Well, this is a known problem to me. Because when creating the URL, host name does not include "/apps". In other sites created in regular ASP.NET page, I would simply hijack the URL creation and replace host with host/apps, that can fix the issue. But I don't know how to do this in MVC world.
Also I am using Telerik controls (for MVC) that also initiate requests by controller and action, which lead to the wrong URL eventually.
The route config is default. I did try to change the rule but all of those only affect the url format after . Nothing could allow me to change the behavior ahead of it.
I have been struggling for days and couldn't see a way out. Appreciate for some advice. Thank you.
I cannot change the proxy rule. That is not an option to me.
Environment: MVC 4, IIS 7.0, if these matter.
Try to add new entry in your route config.
routes.MapRoute(
"Default2", // Route name
"apps/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults,
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Take note of the order.

Routing in MVC application deployed on IIS

I have added the following code
public class RouteController : Controller
{
public ContentResult GetImpression()
{
// Do something
}
}
In RouteConfig class i have added the following
routes.MapRoute(
name: "Impression",
url: "imp",
defaults: new { controller = "Route", action = "GetImpression", id = UrlParameter.Optional }
);
I am expecting my http://mymachine/imp to work. What am i doing wrong? Do i have to do some settings in IIS as well?
For issues with routing I have always found the following tool from Phil Hacck to be invaluable in figuring out where I screwed up with my routing rules and what is getting called. There is nothing special you need to do in IIS to get things working. The one thing you probably should check is to ensure if your Application Pool is using version 4.0 and not version 2.0. Usually when you create a site in IIS it defaults the App Pool to 2.0.

Automatic URL Parameter Encoding Failing

Background
In HomeController.cs I have:
[HttpGet]
public GetPerson(string name)
{
return View(new PersonModel { ... });
}
In Global.asax.cs I have:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Word", "person/{name}",
new { controller = "Home", action = "GetPerson" });
routes.MapRoute(
"Default", "{controller}/{action}",
new { controller = "Home", action = "Index" });
}
In SomePage.cshtml I have, effectively, this:
#{ var name = "Winston S. Churchill"; }
#name
Problem
If I click the link for Winston S. Churchill, I am routed to the URL http://localhost/person/Winston%20S.%20Churchill, which yields the standard 404 page:
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
This only happens if the name variable contains a . (period). All my code works perfectly fine when the name is, for example, Winston Churchill.
How can I make ASP.NET MVC 3 percent-encode the . (period) in the URL?
Or, how can I make the routing work without . (period) being percent-encoded?
Unacceptable Workaround (if presented without justification)
If I change the route to the following, everything works.
routes.MapRoute(
"Word", "person",
new { controller = "Home", action = "GetPerson" });
However, the URL becomes http://localhost/person?name=Winston%20S.%20Churchill, which isn't what I want. I want the name in the path part of the URL, not the query.
Routes which contain a period and unknown extension are interpreted by IIS as static files and not sent through the .NET pipeline. For example, the URL you cite is interpreted as a static file with a %20Churchill extension.
You can force ASP.NET to handle all requests by adding this to web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
You'll also need this, to handle name values that end with a period (as opposed to just containing one):
<system.web>
<httpRuntime relaxedUrlToFileSystemMapping="true" />
</system.web>
All /person/{name} URLs will then be picked up by your ASP.NET code.
If you would rather not use this setting, the easiest workaround would be to use a custom encoding:
name.Replace(".","--")

Asp .net mvc 2 forms authentication not working on iis 6. Need helps determining correct routes

I'm trying to setup Forms Authentication in an asp.net mvc 2 application that will be hosted on IIS 6. There's an issue somewhere in my routing, but I can't pinpoint exactly where it is.
Here is the route entries I'm using to route the mvc requests through the aspx processing on IIS 6. These may or may not be the "right" way, but they do work on the server at current.
routes.MapRoute(
"Default",
"{controller}.aspx/{action}/{id}",
new { action = "LogOn", id = "" }
);
routes.MapRoute(
"Root",
"",
new { controller = "Main", action = "LogOn", id = "" }
);
I've put the [Authorize] attribute on my Main controller.
In my web.config I have:
<authentication mode="Forms">
<forms loginUrl="~/Main.aspx/LogOn" timeout="2880"/>
</authentication>
When the application starts, a blank page loads. The page is quite literally blank. I haven't found a way to amend the loginUrl to actually execute the LogOn action & View for my Main controller.
Edited
Just as an fyi, I've setup my routing based on this article so that the mvc routing can work on IIS 6.
http://www.asp.net/mvc/tutorials/using-asp-net-mvc-with-different-versions-of-iis-cs
I'm guessing the problem here is that the windows form authentication settings aren't syncing with the routes setup so the app can run on IIS 6 via the aspx extension.
Anyone have thoughts on how I could fix this?
Edit 2
Tried adding the following route:
routes.MapRoute(
"Login",
"Login",
new { controller = "Main", action = "LogOn" }
);
Amended the web.config to:
<authentication mode="Forms">
<forms loginUrl="~/Login" timeout="2880"/>
</authentication>
The result is the same white screen as I originally got. It seems like the page doesn't get processed at all. Viewing the source from page generated shows absolutely nothing....no markup...no html declaration....just nothing.
EDIT 3
It seems that I can't seem to get the correct routing configured with the default forms authentication via the web.config. To circumvent this, I've created my own Authorize attribute class. At current, I only care that the user has logged into the system. To accomodate this, I moved the LogOn & LogOff actions to an Account controller. I've remapped the root path to point to this controller. In my custom Authorize attribute, I check to see if the user is logged in and redirect them back to the LogOn page if they aren't. Here is the code:
routes.MapRoute(
"Root",
"",
new { controller = "Account", action = "LogOn", id = "" }
);
And here's the code for the RequireLoginAttribute class I derrived.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequireLoginAttribute : AuthorizeAttribute, IAuthorizationFilter
{
#region IAuthorizationFilter Members
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAuthenticated)
{
//This didn't work...it would try routing to urls like
//http://localhost:1524/Main.aspx/Account.aspx/Logon
//new RedirectResult("Account.aspx/Logon");
//This seems sloppy to me somehow, but it works.
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Account", action = "LogOn" }));
}
}
#endregion
}
Now, I can just apply the [RequireLogin] attribute to the Main controller and it ensures the user must be authenticated.
For the curious, and completeness of this scenerio, I am using the following code in the LogOn action (repository isn't ready yet so things are hard coded):
public ActionResult LogOn(LogOnModel login, String returnUrl)
{
if (ModelState.IsValid)
{
FormsAuthentication.SetAuthCookie(login.UserName, false);
return Redirect(returnUrl ?? Url.Action("NextPage", "Main"));
}
else
{
return View(login);
}
}
The returnUrl is a throwback to the Windows Forms authentication. Since I can't seem to get that working here, the parameter will always be null.
Please, critique this if you see specific areas that need improvement. I'm reading what I can and trying to do things right, so all input is greatly appreciated. Thanks!
If you need the .aspx for the Default Root then why don't you need it for the login route ?
You could do a couple of things then
Actually create a ASP.NET page called Login.aspx and put it in the root of the folder (Authentication will work for your mvc pages as well)
Change your login route to say
routes.MapRoute("Login","Login.aspx",new { controller = "Main", action = "LogOn" });
You should also take a look at to see what route your actually hitting at any time.
http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx
Remember that the order you write your routes in Global matters. It stops checking when it finds one that works so your catch all should be last.
The details I posted in EDIT 3 summarize the solution to this issue. I appreciate all of your input into this question, but I've resolved it. I would have liked to have gotten the "out of the box" forms authentication working, but this solution serves well enough. If we move to IIS 7, I think all of this will become moot anyhow.
Thanks again for your help guys.

Categories

Resources