Response.Redirect() Turns Response.RedirectLocation NULL - asp.net

I have a group of Asp.Net applications that all share a common HttpModule that contains some code like this:
public class MyModule : IHttpModule
{
public void Dispose()
{
}
public void Init( HttpApplication context )
{
context.Error += new EventHandler( context_Error );
}
private void context_Error( object sender, EventArgs e )
{
var lastError = HttpContext.Current.Server.GetLastError();
doSomeStuffWithError(lastError);
var response = HttpContext.Current.Response;
var redirect = GetErrorRedirect(lastError);
response.Redirect(redirect, true);
}
}
This works totally fine for all of my applications except for one. In the case of the one that doesn't work correctly, response.Redirect(...) doesn't seem to work. Instead of the redirect I expect, Asp.Net is redirecting to its standard error page. I've checked the configuration of this application and don't see anything wrong or significantly different from the other applications.
While investigating this issue, I modified the error handler with one more line of code as follows:
private void context_Error( object sender, EventArgs e )
{
var lastError = HttpContext.Current.Server.GetLastError();
doSomeStuffWithError(lastError);
var response = HttpContext.Current.Response;
var redirect = GetErrorRedirect(lastError);
//setting a break point here, i've verified that 'redirect' has a value in all cases
response.Redirect(redirect, true);
var wtf = response.RedirectLocation;
//inspecting the value of 'wtf' here shows that it is null for one application, but equal to 'redirect' in all others.
}
When I set a break point on 'wtf' I'm seeing some strange behavior. For applications that work, wtf contains the same value as redirect. However, for my app that isn't working, wtf is null.
Anyone have any ideas on what would cause wtf to be null in this way?

The overload of Response.Redirect you are using will call Response.End and throw a ThreadAbortException. It says so in the documentation. So, the fact that "it works" in other applications is interesting as it should never execute var wtf = response.RedirectLocation; During a debugging session, it is not surprising that it's null, either, as there is likely some reason that it allows to execute that line during debug.
In addition, it will of course execute the default error page if you have the mode set to On or RemoteOnly for the <customErrors> setting in Web.config unless you clear the error before redirecting. This is by design.
If you need to execute additional code after you have already called Response.Redirect, pass false as the second parameter to avoid the Response.End call and clear the error using HttpContext.Current.ClearError().
Based on your example, I would rewrite your HttpModule like so:
public class MyModule : IHttpModule
{
public void Dispose()
{
}
public void Init( HttpApplication context )
{
context.Error += new EventHandler( context_Error );
}
private void context_Error( object sender, EventArgs e )
{
var context = HttpContext.Current;
var lastError = context.Server.GetLastError();
doSomeStuffWithError(lastError);
var response = context.Response;
var redirect = GetErrorRedirect(lastError);
context.ClearError();
// pass true if execution must stop here
response.Redirect(redirect, false);
// do other stuff here if you pass false in redirect
}
}

Related

What are ways to update ASP.NET page from HttpModule?

I need to update an ASP.NET page from the HttpModule permanently, or from time to time.
Here is the code of IUpdatablePage interface for our page to be updated:
interface IUpdatablePage
{
void Update( string value );
}
Here is the code of HttpModule, I imagine, can be:
void IHttpModule.Init( HttpApplication application )
{
application.PreRequestHandlerExecute += new EventHandler( application_PreRequestHandlerExecute );
}
void application_PreRequestHandlerExecute( object sender, EventArgs e )
{
this._Page = ( Page )HttpContext.Current.Handler;
}
void HttpModuleProcessing()
{
//... doing smth
IUpdatablePage page = this._Page as IUpdatablePage;
page.Update( currentVaue );
//... continue doing smth
}
Here we:
save the current request page in _Page,
get access to IUpdatablePage interface while processing in HttpModule
call Update function passing some currentValue.
Now the page gets the value in Update function.
public partial class MyPage: System.Web.Page, IUpdatablePage
{
void IUpdatablePage.Update( string value )
{
// Here we need to update the page with new value
Label1.Text = value;
}
}
The question is what are the ways to transmit this value to the webform controls so that they would immediately show it in browser?
I suppose any way of refreshing the page: using UpdatePanel, Timer, iframe block, javascript etc.
NOTE, that the request from the page is being processed in HttpModule while refresh.
Please, help with code samples (I'm a web-beginner).
The way to transfer data between Page and HttpModule is to use Application named static objects, that are identified by session id.
The page is update via UpdatePanel triggered by timer.
The code of HttpModule (simplified):
public class UploadProcessModule : IHttpModule
{
public void Init( HttpApplication context )
{
context.BeginRequest += context_BeginRequest;
}
void context_BeginRequest( object sender, EventArgs e )
{
HttpContext context = ( ( HttpApplication )sender ).Context;
if ( context.Request.Cookies["ASP.NET_SessionId"] != null )
{
string sessionId = context.Request.Cookies["ASP.NET_SessionId"].Value;
context.Application["Data_" + sessionId] = new MyClass();
}
}
}

Handling exceptions in global.asax ASP.NET MVC

I have code which is catching all exceptions in Global.asax
protected void Application_Error(object sender, EventArgs e)
{
System.Web.HttpContext context = HttpContext.Current;
System.Exception exc = context.Server.GetLastError();
var ip = context.Request.ServerVariables["REMOTE_ADDR"];
var url = context.Request.Url.ToString();
var msg = exc.Message.ToString();
var stack = exc.StackTrace.ToString();
}
How I can get controller name where this error happened
How I can get request client IP?
And can I filter exceptions? I dont need 404, 504.... erors
thanks
Global.asax has not notion of controllers and actions, so I believe there is no an API for retrieving controller and action names. However you might give a try for resolving request URL:
HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current);
UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
RouteData routeData = urlHelper.RouteCollection.GetRouteData(currentContext);
string action = routeData.Values["action"] as string;
string controller = routeData.Values["controller"] as string;
To get the user IP you can use UserHostAddress property:
string userIP = HttpContext.Current.Request.UserHostAddress;
To filter out HTTP exceptions that you are not going to handle you can use something like:
HttpException httpException = exception as HttpException;
if (httpException != null)
{
switch (httpException.GetHttpCode())
{
case 404:
case 504:
return;
}
}
One last remark about exception handling - it is not a best practice to do it at global level when there is a way to perform it more locally. For instance in ASP.NET MVC base Controller class has a method:
protected virtual void OnException(ExceptionContext filterContext)
which, when overridden, will give you full control on the occurred exception. You can have all the info that is available for you in Global.asax plus ASP.NET MVC specific features like a reference to controller, view context, route data etc.
i used like this it's below
you can get user ip like this
var userip = context.Request.UserAgent;
and you can get your url where this error happened like this
var ururl = System.Web.HttpContext.Current.Request.Url;
i think this will help you...
I'd take a different tack and put use an attribute on your controllers (or base controller if you have one)
public class HandleErrorAttributeCustom : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
//you can get controller by using
context.Controller.GetType()
//however, I'd recommend pluggin in Elmah here instead
//as it gives this easily and also can has filtering
//options that you want
}
}

Asp.net global output cache

Last few days I thinkin about output cache in asp.net. In my task I need to implement output cache for the very big project. After hours of searching I did not find any examples.
Most popular way to use output cache is declarative, in this case you need to write something like this on the page which you want to cache.
But if you need to cache whole site you must write this on all pages or master pages on project. It is madness. In this case you cant store all configuration in one place. All page have his own configurations..
Global.asax could help me, but my site contains about 20 web progects and ~20 global.asax files. And i don't want copy same code to each project.
For these reasons, i made decision to create HTTPModule.
In Init method i subscribe to two events :
public void Init(HttpApplication app)
{
app.PreRequestHandlerExecute += new EventHandler(OnApplicationPreRequestHandlerExecute);
app.PostRequestHandlerExecute += new EventHandler(OnPostRequestHandlerExecute);
}
In method "OnPostRequestHandlerExecute" I set up output caching parameters for each new request :
public void OnPostRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpCachePolicy policy = app.Response.Cache;
policy.SetCacheability(HttpCacheability.Server);
policy.SetExpires(app.Context.Timestamp.AddSeconds((double)600));
policy.SetMaxAge(new TimeSpan(0, 0, 600));
policy.SetValidUntilExpires(true);
policy.SetLastModified(app.Context.Timestamp);
policy.VaryByParams.IgnoreParams = true;
}
In "OnApplicationPreRequestHandlerExecute" method I set calback method to cache validation:
public void OnApplicationPreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
app.Context.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(Validate), app);
}
And last part - callback validation method :
public void Validate(HttpContext context, Object data, ref HttpValidationStatus status)
{
if (context.Request.QueryString["id"] == "5")
{
status = HttpValidationStatus.IgnoreThisRequest;
context.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(Validate), "somecustomdata");
}
else
{
status = HttpValidationStatus.Valid;
}
}
To attach my HttpModule I use programmatically attach method :
[assembly: PreApplicationStartMethod(typeof(OutputCacheModule), "RegisterModule")]
This method works perfectly, but I want to know is there other ways to do this.
Thanks.
Try seeing if IIS caching provides what you need.
http://www.iis.net/configreference/system.webserver/caching

Call the default asp.net HttpHandler from a custom handler

I'm adding ASP.NET routing to an older webforms app. I'm using a custom HttpHandler to process everything. In some situations I would like to map a particular path back to an aspx file, so I need to just pass control back to the default HttpHandler for asp.net.
The closest I've gotten is this
public void ProcessRequest(HttpContext context) {
// .. when we decide to pass it on
var handler = new System.Web.UI.Page();
handler.ProcessRequest(context);
MemoryStream steam = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
handler.RenderControl(htmlWriter);
// write headers, etc. & send stream to Response
}
It doesn't do anything, there's nothing output to the stream. MS's documentation for System.Web.UI.Page (as an IHttpHandler) say something to the effect of "do not call the ProcessRequest method. It's for internal use."
From looking around it seems like you can do this with MVC, e.g. : MvcHttpHandler doesn't seem to implement IHttpHandler
There is also this thing System.Web.UI.PageHandlerFactory which appears that it would just produce a Page handler for an aspx file, but it's internal and I can't use it directly.
This page: http://msdn.microsoft.com/en-us/library/bb398986.aspx refers to the "default asp.net handler" but does not identify a class or give any indication how one might use it.
Any ideas on how I can do this? Is it possible?
Persistence pays off! This actually works, and since this information seems to be available pretty much nowhere I thought I'd answer my own question. Thanks to Robert for this post on instantiating things with internal constructors, this is the key.
http://www.rvenables.com/2009/08/instantiating-classes-with-internal-constructors/
public void ProcessRequest(HttpContext context) {
// the internal constructor doesn't do anything but prevent you from instantiating
// the factory, so we can skip it.
PageHandlerFactory factory =
(PageHandlerFactory)System.Runtime.Serialization.FormatterServices
.GetUninitializedObject(typeof(System.Web.UI.PageHandlerFactory));
string newTarget = "default.aspx";
string newQueryString = // whatever you want
string oldQueryString = context.Request.QueryString.ToString();
string queryString = newQueryString + oldQueryString!="" ?
"&" + newQueryString :
"";
// the 3rd parameter must be just the file name.
// the 4th parameter should be the physical path to the file, though it also
// works fine if you pass an empty string - perhaps that's only to override
// the usual presentation based on the path?
var handler = factory.GetHandler(context, "GET",newTarget,
context.Request.MapPath(context,newTarget));
// Update the context object as it should appear to your page/app, and
// assign your new handler.
context.RewritePath(newTarget , "", queryString);
context.Handler = handler;
// .. and done
handler.ProcessRequest(context);
}
... and like some small miracle, an aspx page processes & renders completely in-process without the need to redirect.
I expect this will only work in IIS7.
I'm you're using Routing in webforms you should be able to just add an ignore route for the specific .aspx files you want. This will then be handled by the default HttpHandler.
http://msdn.microsoft.com/en-us/library/dd505203.aspx
Another option is to invert the logic by handling the cases in which you do NOT want to return the default response and remap the others to your own IHttpHandler. Whenever myCondition is false, the response will be the "default". The switch is implemented as an IHttpModule:
public class SwitchModule: IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += app_PostAuthenticateRequest;
}
void app_PostAuthenticateRequest(object sender, EventArgs e)
{
// Check for whatever condition you like
if (true)
HttpContext.Current.RemapHandler(new CustomHandler());
}
public void Dispose()
}
internal class CustomHandler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.Write("hallo");
}
public bool IsReusable { get; }
}

Best way to abort/cancel action and response from ActionFilter

Best way to abort/cancel action from ActionFilter
I've got this ActionFilter, and it's suppose to end the connection immediately and return a 401 Unauthroized:
public class SignInRequired : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// User is verified, continue executing action
if (Acme.Web.CurrentUser != null)
{
return;
}
// End response with 401 Unauthorized
var response = HttpContext.Current.Response;
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.End();
// Prevent the action from actually being executed
filterContext.Result = new EmptyResult();
}
}
I learned how you can cancel the action from executing by setting 'context.Result = new EmptyResult()` here, but I'm not sure if this is the best way to flush the response and close the connection.
Setting the response will mean the action doesn't get called.
public override void OnActionExecuting(HttpActionContext actionContext)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
As other answers have said, though, authentication should be done with an AuthorizeAttribute (Docs for Web.API or for MVC).
On .net core 2.2, 3.0 and 3.1 and .net 5 the below example works fine
public override void OnActionExecuting(ActionExecutingContext context)
{
context.Result = new UnauthorizedObjectResult("user is unauthorized");
}
The answer that #OdeyinkaOlubunmi is correct for Web API or specifically System.Web.Http.Filters.ActionFilterAttribute but it can't be used for System.Web.Mvc.ActionFilterAttribute. AuthorizeAttribute and overriding AuthorizeCore is a good way to go but if you use #Vadim's example for a GlobalFilter you will end up with the following error in a standard configuration:
HTTP Error 404.15 - Not Found The request filtering module is
configured to deny a request where the query string is too long.
This is because the default /Login?ReturnUrl= will keep appending new values until the query string causes an exception.
The way I have solved it for MVC is like this:
public class DebugActionFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext actionContext)
{
actionContext.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
return;
}
}
You can set the result of filterContext for the Exception page like this:
filterContext.Result = new RedirectResult("~/Error/Unauthorized");
See more details here on section Canceling Filter Execution
You probably want to make it an AuthorizeAttribute. That will set the result to be an UnAuthorizedResult automatically, plus it has the benefit of being run before any other filters. Alternatively you can set the Result to be a new HttpUnauthorizedResult
public class SignInRequiredAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return !Acme.Web.CurrentUser != null;
}
}
using .net core 2.1 the solutions above did not work for me , so i tried this and it worked :-
context.HttpContext.Response.StatusCode = 401;
return;
if there is better solutions for .net core 2.1 i am open for suggestions

Resources