I am using Context.RewritePath() in ASP.NET 3.5 application running on IIS7.
I am doing it in application BeginRequest event and everything works file.
Requests for /sports are correctly rewritten to default.aspx?id=1, and so on.
The problem is that in my IIS log I see GET requests for /Default.aspx?id=1 and not for /sports.
This kind of code worked perfectly under IIS6.
Using Microsoft Rewrite module is not an option, due to some business logic which has to be implemented.
Thanks.
EDIT:
It seems my handler is too early in the pipeline, but if I move the logic to a later event, than the whole rewrite thing doesn't work (it's too late, StaticFileHandler picks up my request).
I googled and googled, asked around, can't believe that nobody has this problem?
EDIT:
Yikes! Here's what I found on the IIS forum:
"This is because in integrated mode, IIS and asp.net share a common pipeline and the RewritePath is now seen by IIS, while in IIS6, it was not even seen by IIS - you can workaround this by using classic mode which would behave like IIS6."
Final update: Please take a look at my answer below, I've updated it with results after more than a year in production environment.
After some research, I've finally found a solution to the problem.
I have replaced the calls to Context.RewritePath() method with the new (introduced in ASP.NET 3.5) Context.Server.TransferRequest() method.
It seems obvious now, but not event Senior Dev Engineer on IIS Core team thought of that.
I've tested it for session, authentication, postback, querystring, ... issues and found none.
Tommorow I'll deploy the change to a very hight traffic site, and we'll soon know how it actually works. :)
I'll be back with the update.
The update: the solution is still not entirely on my production servers but it's tested and it does work and as far as I can tell so far, it's a solution to my problem. If I discover anything else in production, I will post an update.
The final update: I have this solution in production for over a year and it has proven to be a good and stable solution without any problems.
You could set the path back to the original value after the request has been processed but before the IIS logging module writes the log entry.
For example, this module rewrites the path on BeginRequest and then sets it back to the original value on EndRequest. When this module is used the original path appears in the IIS log file:
public class RewriteModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += OnBeginRequest;
context.EndRequest += OnEndRequest;
}
static void OnBeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.Items["OriginalPath"] = app.Context.Request.Path;
app.Context.RewritePath("Default.aspx?id=1");
}
static void OnEndRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
var originalPath = app.Context.Items["OriginalPath"] as string;
if (originalPath != null)
{
app.Context.RewritePath(originalPath);
}
}
public void Dispose()
{
}
}
I've had exactly the same problem. One way around this is to use Server.Transfer instead of Context.RewritePath. Server.Transfer doesn't restart the entire page lifecycle so the original URL will still be logged. Be sure to pass "true" for the "preserveForm" parameter so that the QueryString and Form collections are available to the 2nd page.
Old question, but I found I did not encounter your problem when I did the following:
a) A rewrite rule in web.config to direct all requests to /default.aspx, eg:
<rule name="all" patternSyntax="Wildcard" stopProcessing="true">
<match url="*"/>
<action type="Rewrite" url="/default.aspx"/>
</rule>
b) Called RewritePath in the Page_PreInit event of default.aspx, to rewrite the URL and querystring as what was passed in the request (ie. the location that doesn't exist).
For example, I request "/somepage/?x=y" (which doesn't exist).
a) Web.config rule maps it to /default.aspx
b) Page_PreInit rewrites it back to "/somepage/?x=y".
The result of this, in IIS 7 (Express and production) is that the server log reflects "/somepage" for stub and "x=y" for query, and all the Request object properties reflect the requested (non-existent) URL (which is what I wanted).
The only weird effect is, in IIS Express, the log item is written twice. However this doesn't happen in production (Windows Server 2008 R2).
Related
I have a web service deployed on IIS 8.5 (Windows Server 2012 R2) which has to be called passing four arguments. They are not optional, and are to be included directly in the URL, without a query string, as such:
.. api/myservice/argument1/argument2/argument3/argument4
If I try to call it with the URL written above, it answers and gives the expected response. However, a 404 - Not Found error is given instead when trying to use real production-like arguments as such:
api/myservice/AAAA_AAAAA.AAA_AAA_AA_AA_AAA_000000_000000_000000/333/AAA/AAA.AAA.AA.AA.AAA.000000.000000.000000
I thought the multiple dots raised the issue, so I replaced each one of them with %2E, but nothing changed.
I've searched for already answered questions: I tried this and this, to no avail.
What is the problem with that production-like URL?
Here is the code of the API:
[RoutePrefix("api/myservice")]
public class MyServiceController : ApiController
{
[HttpGet]
[Route("{argument1}/{argument2}/{argument3}/{argument4}")]
public string StartValidation(string argument1, string argument2, string argument3, string argument4)
{
// operations...
}
}
I traced the request with IIS Failed Request Tracing as suggested in an answer, but I'm not able to find a clue in the resulting log:
Can anyone help me?
Enable failed request tracing on IIS to check.
https://learn.microsoft.com/en-us/iis/configuration/system.applicationhost/sites/site/tracefailedrequestslogging
Good possibility that IIS is rejecting these as part of URLScan (if enabled), but FRT will confirm.
First try to deploy it to your local Iis, if it works, means that your production can have a different configuration at the level of the IIS, or the compiled code is not the same as the one you have and you need to redeploy the api.
If the api fails at the level of your local IIS and it runs properly from the vs(if you pass any parameter with the postman) you hit a backend breakpoint, then you have a wrong configuration on your deployment on your local IIS.
If you never hit the breakpoint you have an error at the level of your code.
Hope this helps
I finally solved this. I added a piece code, provided by this answer to a quite identical question, to the web.config, but a different issue arose: it seemed IIS had to be run in "Integrated Mode". Thanks to the clue given by this answer to a question about this subsequent problem, I switched the Managed pipeline mode of the application pool where the API was deployed from Classic to Integrated, which allowed that code to work. The web API finally accepts arguments with periods.
I'm using 51 degrees on my MVC site as an adaptive approach to serving my pages. All is working well.
I have recently added output caching and I use the VaryByCustom to check whether it is mobile or desktop:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
// this is for the output cache
if (context != null)
{
switch (custom)
{
case "Mobile":
return GetMobileCustomString(context);
}
}
return base.GetVaryByCustomString(context, custom);
}
private static string GetMobileCustomString(HttpContext context)
{
if (context.Request.Browser.IsMobileDevice)
{
return "IsMobile";
}
else
{
return "IsDesktop";
}
}
However I have run into a problem that if the first mobile user to browse the site has requested the desktop version, this will be cached for all mobile users.
I need to change the GetMobileCustomString to include a check for if it is a mobile requesting a desktop site. Is there any way to do this?
Update
As a bounty has been opened on this, I thought I would offer an update:
Firstly, it was not the first load causing the desktop page to be cached as I had initially thought, so having done a lot of searching, research and testing on this, I feel the desktop page should never be cached for the mobile version (if you are on MVC 5). I have stepped through the code when doing a mobile request and when it gets to the vary by custom, it shows context.Request.Browser.IsMobileDevice as false.
Not sure what is causing the desktop site to be cached for the mobile site - perhaps it is a hangover from the bug in MVC 4. It just seems to be random (ie, one day it will be fine and then another day it will be serving the desktop site for some reason) and recycling the app pool will always fix it.
I also found that I could get the overriden browser by using:
using System.Web.Wepages;
context.Request.RequestContext.HttpContext.GetOverriddenBrowser();
But it didn't seem to be much use
It seems to me like what you are doing should work, provided you are storing the cache at the client side.
You should be using the OutputCache attribute on your controller actions in the following manner:
[OutputCache(Duration = 5, VaryByCustom = "Mobile", Location = OutputCacheLocation.Client)]
(The duration is up to you)
That being said, browser detection is based on HTTP headers so nothing is going to help you if the requesting browser is sending headers for a different agent.
The other option is to use feature detection instead.
I hope that helps.
Within the 51degrees.config there is a setting
<redirect devicesFile="" timeout="20" firstRequestOnly="true"
This firstrequestonly may be confusing your caching system so treating the first mobile device as the first request, and all other mobiles as the same session of the original mobile device. Try setting this to false and see if that helps solve your issue.
I have a class that implements IHttpModule. This is a support class to help my application defend against DDOS attacks. After implementing the BeginRequest method, I tried to debug my code and for some reason, each time that I debug the class, I have multiple threads in Visual studio. I can't understand why, all of the sudden, while running this application on my local machine, I'm getting several threads, and it happens only in this class.
The HttpModule probably intercepts all requests to your application, including files (js, css, images, etc.)
Take a look at the Request object of each request, and look at the Url property to see what is happening.
Edit:
HttpModules are active very early in the request flow, and they will often get hit by most requests for the server, so keep the code in the HttpModule to a minimum. An example: if you're making permissions on files, make sure the request is actually hitting a file (ie. the requested url starts with /files/). Whenever possible, cache data for use in HttpModules, don't go to the database for each and every request in a HttpModule!
The reason why you probably get less hits in your actual application, is that even requests for images, js files, css files, etc. might make a hit in the HttpModule, but in your application, only requests meant for the application will hit your break points (aspx, asmx, etc. for Web Forms and recognized routes in ASP.NET MVC).
To get a look at what requests you're handling in a HttpModule, look at the value of the url variable:
void context_BeginRequest(object sender, EventArgs e) {
HttpApplication app = (HttpApplication)sender;
String url = app.Request.Url.OriginalString;
}
I am working on a c# .NET website in which the user can click on a link and get redirected to another web page in a separate website also owned by us. The code is very easy to understand, there is a switch followed by a call to Response.Redirect(the_url_we_want_to_go_to).
I have debugged it numerous times and I can confirm that when the debugger hits this redirect line that the parameter is correct. It points to the QA version of this other website. qa.samplesite.com lets say. However, the browser does not go there. The browser instead hits the test environment instead. Lets call it test.samplesite.com. This is the problem.
I understand there are a million things in between the app servers these two separate websites are on, but maybe one of you has seen something like this before. More specifically, is there a way to catch outbound traffic in the debugger or is there a way to see outbound traffic on the app server itself (in IIS)? I am familiar with intercepting inbound traffic inside of httpmodules. Maybe this isnt a stackoverflow question...
Thanks for your help!
Use the very nice HTTP sniffer "Fiddler". It will allow you to see all HTTP requests. You should verfiy that a) the redirect target is correct (it might be overwritten later in the request pipeline. A Response.Redirect is not the final word) and b) that you don't have a second redirect after the first one.
You can try using the overload of Response.Redirect as
Response.Redirect("url_here", true);
This will stop the response on current page (as endResponse is set to true) and redirect to the url.
If you still have the issue, then this might be some name resolution error.
Check to see you your hosts files in the windows directory found here
C:\Windows\System32\drivers\etc\hosts
Hope this will do it.
I finally figured a similar issue I had. It was quite silly. I had copied some .aspx pages making minor changes. The page where I tried to redirect the client, had it's "CodeBehind" setting wrong. So it redirected to the right page, but loaded the CodeBehind from another page!
I am trying to build a webservice that manipulates http requests POST and GET.
Here is a sample:
public class CodebookHttpHandler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.HttpMethod == "POST")
{
//DoHttpPostLogic();
}
else if (context.Request.HttpMethod == "GET")
{
//DoHttpGetLogic();
}
}
...
public void DoHttpPostLogic()
{
...
}
public void DoHttpGetLogic()
{
...
}
I need to deploy this but I am struggling how to start. Most online references show making a website, but really, all I want to do is respond when an HttpPost is sent. I don't know what to put in the website, just want that code to run.
Edit:
I am following this site as its exactly what I'm trying to do.
I have the website set up, I have the code for the handler in a .cs file, i have edited the web.config to add the handler for the file extension I need. Now I am at the step 3 where you tell IIS about this extension and map it to ASP.NET. Also I am using IIS 7 so interface is slightly different than the screenshots. This is the problem I have:
1) Go to website
2) Go to handler mappings
3) Go Add Script Map
4) request path - put the extension I want to handle
5) Executable- it seems i am told to set aspnet_isapi.dll here. Maybe this is incorrect?
6) Give name
7) Hit OK button:
Add Script Map
Do you want to allow this ISAPI extension? Click "Yes" to add the extension with an "Allowed" entry to the ISAPI and CGI Restrictions list or to update an existing extension entry to "Allowed" in the ISAPI and CGI Restrictions list.
Yes No Cancel
8) Hit Yes
Add Script Map
The specified module required by this handler is not in the modules list. If you are adding a script map handler mapping, the IsapiModule or the CgiModule must be in the modules list.
OK
edit 2: Have just figured out that that managed handler had something to do with handlers witten in managed code, script map was to help configuring an executable and module mapping to work with http Modules. So I should be using option 1 - Add Managed Handler.
I know what my request path is for the file extension... and I know name (can call it whatever I like), so it must be the Type field I am struggling with. In the applications folder (in IIS) so far I just have the MyHandler.cs and web.config (Of course also a file with the extension I am trying to create the handler for!)
edit3: progress
So now I have the code and the web.config set up I test to see If I can browse to the filename.CustomExtension file:
HTTP Error 404.3 - Not Found
The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.
So in IIS7 I go to Handler Mappings and add it in. See this MSDN example, it is exactly what I am trying to follow
The class looks like this:
using System.Web;
namespace HandlerAttempt2
{
public class MyHandler : IHttpHandler
{
public MyHandler()
{
//TODO: Add constructor logic here
}
public void ProcessRequest(HttpContext context)
{
var objResponse = context.Response;
objResponse.Write("<html><body><h1>It just worked");
objResponse.Write("</body></html>");
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
I add the Handler in as follows:
Request path: *.whatever
Type: MyHandler (class name - this appears correct as per example!)
Name: whatever
Try to browse to the custom file again (this is in app pool as Integrated):
HTTP Error 500.21 - Internal Server Error
Handler "whatever" has a bad module "ManagedPipelineHandler" in its module list
Try to browse to the custom file again (this is in app pool as CLASSIC):
HTTP Error 404.17 - Not Found
The requested content appears to be script and will not be served by the static file handler.
Direct Questions
1) Does the website need to be in CLASSIC or INTEGRATED mode? I don't find any reference of this in the online material, whether it should be either.
2) Do I have to compile the MyHandler.cs to a .dll, or can I just leave it as .cs? Does it need to be in a bin folder, or just anywhere in root?
RE your questions:
I don't know the answer to the first one (CLASSIC or INTEGRATED); but I can help with the second...
Yes you'll need to compile it first. I have never tried deploying dll's to anywhere other than the bin, given that that's the standard I would be suspect in putting them anywhere else even if it did work.
The way I deploy HttpHandlers is quiet straight forward - all the hard work's done in web.config, I'v enever had to go into IIS to change any settings.
For a start, for the http request to be handled by ASP.NET you need to use a request suffix that's already piped to ASP.NET - like .aspx or ashx. If you want to use something else you will need to config IIS to do this, as per your managed handler img above.
I tend to use .ashx e.g: http://localhost/foo/my/httphandler/does/this.ashx
All you need to do (assuming you've compiled athe HttpHandler into a DLL and deployed it to the site) is add the necessary config.
<configuration>
<system.web>
<httpHandlers>
<add verb="*"
path="*.ashx"
type="MyApp.PublishingSystem.HttpHandlers.GroovyHandler, MyApp.PublishingSystem" />
</httpHandlers>
</system.web>
</configuration>
Obviously (?) you can change / restrict the scope using the path, e.g:
path="*.ashx"
path="*ListWidgets.ashx"
path="*Admin/ListWidgets.ashx"
More info here: http://msdn.microsoft.com/en-us/library/ms820032.aspx
An important gotcha to look out for is the order in which you declare your HttpHandlers in the config; from what I remember ones declared first take precedent. So in this example...
<add verb="*" path="*foo.ashx" type="MyApp.PublishingSystem.HttpHandlers.FooHandler, MyApp.PublishingSystem" />
<add verb="*" path="*.ashx" type="MyApp.PublishingSystem.HttpHandlers.GroovyHandler, MyApp.PublishingSystem" />
...the groovy handler will handle all HttpRequests except any that end in foo.ashx
By the way, I make use of HttpHanldrs in my open source .net CMS / app framework, you might find some helpful code there (?): http://morphfolia.codeplex.com/
Make sure the app pool's .NET Framework Version is set correctly...
I deployed a .NET 4.0 web app on a .NET 2.0 app pool and got this error. Set the app pool to v4.X and the ashx was served like a champ.