ASP.NET MVC 2 app
I have two actions on my controller (Toons):
[GET] List
[POST] Add
App is running on IIS7 integration mode, so /Toons/List works fine. But when I do POST (that redirects to /Toons/List internally) it redirects (with 302 Object Moved) back to /Toons/Add.
The problem goes away if I use .aspx hack (that works in IIS6/IIS7 classic mode).
But without .aspx - GET work fine, but POST redirects me onto itself but with GET.
What am I missing?
I'm hosting with webhost4life.com and they did change IIS7 to integrated mode already.
EDIT: The code works as expected using UltiDev Cassini server.
EDIT: It turned out to be trailing-slash-in-URL issue. Somehow IIS7 doesn't route request properly if there is no slash at the end.
EDET: Explanation of the behavior
What happens is when I request (POST) /Toons/List (without trailing slash), IIS doesn't find the handler (I do not have knowledge to understand how exactly IIS does URL-to-handler mapping) and redirects the request (using 302 code) to /Toons/List/ (notice trailing slash).
A browser, according to the HTTP specification, must redirect the request using same method (POST in this case), but instead it handles 302 as if it is 303 and issues GET request for the new URL.
This is incorrect, but known behavior of most browsers.
The solution is either to use .aspx-hack to make it unambiguous for IIS how to map requests to ASP.NET handler, or configure IIS to handle everything in the virtual directory using ASP.NET handler.
Q: what is a better way to handle this?
You have control over your code.
change all pages that make a post without the trailing slash to post to the correct page.
if its 3rd party clients, than return an exception, that they should fix the bug.
this is expected behaviour, and its not your job to recover everything that can happen.
but you should give good hints (e.g. exception message, instead of a weird error or redirect.)
Related
I have a single ASP.NET MVC app which uses areas to deliver different functionality depending which url is hit. For example
www.domain.com - Website Area
app.domain.com - Application Area
*.domain.com - Client Area
So, the point is that depending on the incoming url, we route you to a different MVC Area. This is all done using Routing with some extensions and works great.
Now, if I enable outputcache on the Index() Action for my www default route, the next time i hit app.domain.com, i get the cached version of the www domain. I checked using fiddler and the response is a 200 OK so it's definately hitting the server. However, the logging in my custom routing tells me it's not hitting that code.
So, does OutputCache not work based off the uri and instead uses some other algorithm?
Thanks
[OutputCache(VaryByHeader="Host")] should help.
The behavior will depend on where you decided to store the cache (Location property). Ifv you stored the cache on the server (OutputCacheLocation.Server) then the result from the execution of the action will be stored on the server and when a subsequent request is made to this action, the server will be hit and it will directly return the cached version without executing the controller action which is the behavior you describe.
If you store the cache on the client (OutputCacheLocation.Client), then the cache will be kept on the client browser. In this case if a subsequent request is made to the same action, the client will no longer hit the server but will directly serve the page from its cache. And remember that if you hit F5 in your browser you will expire the cache for the given page, so the server will be hit.
i was wondering about the following:
i can define in IIS what to do with page not founds / 404, and also in my app i can put it in my CustomErrors section or just handle it in code.
Now as i assume IIS always gets the request first, when does it handle the 404 for itself, and when does it let it pass through to my app?
And a side-question: can IIS actually know if a request in asp.net MVC is a 404 because it might or it might not me mapped via any route?
IIS looks at the request extension. If there is a module registered to handle the type of request that came in, then it will forward the request to that module.
For example, if you request foo.jpg from your server, then IIS has a module built in to handle image/jpg content. If that module can't find the file then it returns a 404.
Same thing here. Whatever your MVC handlers don't look for (ie: images), then IIS will take care of in another manner.
can IIS actually know if a request in asp.net MVC is a 404 because it might
or it might not me mapped via any route?
I think it all depends on how your controller factory handles the unmapped request. The DefaultController factory seems to throw an HttpException with code 404 when it cannot find a route and let IIS handle the error display. You can play with this and see it in action by creating your own controller factory.
For example, add the following line to your Application_Start
ControllerBuilder.Current.SetControllerFactory(new TestControllerFactory());
and add this class to a brand new MVC project:
public class TestControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
//throw new Exception("Oops!"); // yellow screen of death
throw new System.Web.HttpException(404, "Oops not found!"); // bubbles up to IIS
}
return base.GetControllerInstance(requestContext, controllerType);
}
}
Navigate to your project to http://localhost/MvcApplication1/unmapped and see what happens when you throw an HttpException with code 404 versus when you throw a regular Exception (or even an HttpException with a code other than 404)
Make sure you are running your project under IIS (rather than the VS Dev Server) as the they handle these things different.
Now as i assume IIS always gets the request first, when does it handle the 404 for itself, and when does it let it pass through to my app?
Although the request is initially handled by ISS, it passed through to the MVC application. If a file is not found, an HttpException is thrown which bubbles back to IIS. If a file is found, it is served directly bypassing routing.
However, you can modify the behaviour by adjusting the the RouteExistingFiles property. If I'm not mistaken though you will need to create a route to handle all static content when the property is set to true (default is false). However, as a post here suggests, as per Steve Sanderson, that the routing system will first check if the file exists on disk. (See this related SO question which provides better clarification, especially the comments).
Try it yourself. Use the Application_Error() and Application_EndRequest() events in the Global.asax file and inspect the HttpContext.Current.Response object to to see what the final responses are when content is being served.
And a side-question: can IIS actually know if a request in asp.net MVC is a 404 because it might or it might not me mapped via any route?
This is where route configuration comes into play. Since MVC will check to see if the file exists first, if it does, it gets served directly and routing is bypassed. With regards to Controllers and actions, the same will apply. Eg. /SomeController/ActionThatDoesExist is first checked to verify if it is a physical file. Clearly this is not a file and a 404 exception will be returned from the application.
The third aspect I think that may be related to this question is how MVC and IIS work together. By this I am referring to Integrated Mode and Classic Mode. An awesome explanation can be found here.
IIS always handles the request and then forwards it to the MVC application. This is where it is basically decided how to handle it.
If their is already a physical file on the disk, then the whole Routing is bypassed and the file is served. If it can't find the file then it tries matching the Routing. If nothing works then the MVC application may handle the 404 otherwise it throws the HTTPException and IIS handles the 404.
I believe even in Webforms, the 404 scenario is pretty much the same internally. Only difference being the target is always a physical disk but the request still goes to ASP.NET Webforms, incase you want to handle the 404 yourself.
You will want to keep the execution within your MVC app and like others have said, usually IIS will pass the request into MVC first and only then if MVC passes a 404 exception up high enough will IIS get it back and apply it's decision making process.
The key is: handle 404's in MVC properly!
i'm trying to goto the following url :-
http://user1:pass1#localhost:1234/api/users?format=xml
nothing to complex. Notice how i've got the username/password in the url? this, i believe, is for basic authentication.
When I do that, the Request Headers are MISSING the 'Authorize' header. Er... that's not right :(
I have anonymous authentication only setup on the site. I don't want to have anon off and basic turned on .. because not all of the site requires basic.. only a few action methods.
So .. why is this not working? Is this something to do with the fact my code is not sending a 401 challenge or some crap?
For What It's Worth, my site is ASP.NET MVC1 running on IIS7 (and the same thing happens when i run it on cassini).
Update:
If this is an illegal way of calling a resource using basic auth (ala security flaw) .. then is this possible to do, for an ASP.NET MVC website .. per action method (and not the entire site, per say)?
If you want to use basic authentication, the first request to the resource needs to return a HTTP 401 error code, and set a WWW-Authenticate header. This will instruct the browser to actually send those credentials.
You mentioned you're using ASP.NET MVC. You might be able to do this via the web.config, but I'm not sure on the exact mechanics.
My company makes a product called the Neokernel Web Server (http://www.neokernel.com), it is an ASP.NET web server with support for basic authentication among other features.
You specify protected resources in an apache-style config file so you could put your "protected" actions in a folder requiring authentication and put everything else in the root / unprotected. Look at the "http.authentication" file installed in the Neokernel root directory for an example, or at the authentication samples in the demos.zip file.
So far, my https deployments have commonly involved a naive lockdown of the entire site with https and provide an http-to-https redirect on the web server.
I now plan to have a single ASP.NET MVC site (on the cloud) that will contain both http and https pages. So, the site will have 2 conceptual (not physical) zones providing for both secure and non-secure requests.
Configuration-wise, I have set up input ports for both 80 and 443 and the site accepts both requests.
Is there any way I can flip protocol to https for any call that goes to an action that belongs in the secure zone? For instance, the kind of things that action filters can do.
Thanks much.
edit: Please note that the whole idea of this is to avoid using absolute urls on the form action attribute because of portability issues and because the user will not see the https:// assurance visual cues on the browser.
P
You might want to take a look at the MVC futures assembly from Microsoft available for download here.
This has a FilterAttribute, RequireSslFilterAttribute that allows you to easily tag Action methods in your controller that require SSL - e.g.
[RequireSsl(Redirect=true)]
public ActionResult LogOn()
{
return View();
}
The optional redirect parameter will cause the request to be redirected to the same URL but via https instead of http if required.
WARNING: As Daniel points out though, by the time you hit this Action it may already be too late if data was posted to a non secure version of the page - it is already potentially compromised, so you still need to exercise care when using this and make sure all sensitive data is sent via https. (I just noticed your comment to Daniel, you obviously understand this, I'll leave the warning here for anyone else who stumbles upon this though!)
EDIT: As Luke points out, in MVC2 this attribute is now part of the core framework and is renamed to [RequireHttps]
Is there any way I can flip protocol to https for any call that goes to an action that belongs in the secure zone?
The short answer is no, once the request has come via http, it has already been potentially compromised. You can require that certain calls come via the HTTPS (not sure how to do that as I have not done ASP.Net for awhile) and send an error if they do not. The key is to decide when you want the application to make the jump, ie during login and choose the HTTPS as the action for those forms. Is that what you meant by 'action filters'?
This one is causing me a few nightmares as I'm on the live box trying to work out what is going wrong!
If someone accesses our ASP.Net MVC website with the full URL http://www..net all is OK. If they go to: http://.net then our custom error page is shown. This used to work OK before we moved the site to MVC.
We do have an Application_OnError event in the Global.asax but I know that is not being hit in this situation, as I log to the event log and that is not happening.
If I switch custom errors off in the web.config, the site behaves correctly!
We're using the MVC Beta at the moment. Edit: We're running on IIS6 and using the MVC routing for friendly URLs.
This is impossible to test locally which is fustrating as it only happens on live without the www. I wonder if it is something to do with routing......
Thanks!
The problem is too vague at this stage for me to be able to give you a good answer but I would look firstly at your URL rewriting - what version of IIS are you using? If IIS5 or 6, are you using Isapi Rewrite? This could be interfering with your response.
As for why the error goes away when you turn customErrors off, well, I have no idea sorry.
On a side-note, if you're concerned with Google ranking, you may want to use a rewriting tool (like Isapi Rewrite and I think built-in to IIS7) to send an automatic redirect (HTTP 301 response) that will send users from the non-www version to the www version. Google sees both of these as individual sites with duplicate content and this will dilute your Page-Rank. This will also avoid the problem you're experimenting altogether as users will only ever see the www version.
Also, I'm not sure if Application_Error is really the best way to deal with errors in ASP.Net MVC. Do some research into the HandleError Action Filter as this to see if this might provide you with a better approach to error handling. Check out Scott Gu's post on this for more info.
I hope this helps.
Cheers,
Zac
i was having the same problem in my MVC .net site but it worked out for me when i enter both domain.com and www.domain.com in the host header in IIS.