I have enabled page caching for my ASPX pages with
<%# OutputCache Duration="7200" VaryByParam="*" Location="Server" %>
However, the next time the page is regenerated, if there happens to be an error in the page, that also gets cached, and the site continues to display the page with errors in it for the next 7200 seconds or until some dependancy flushes the cache.
Currently I tried adding the sites error log as a file dependancy, so that anytime an error is logged, pages are refreshed. However, that causes the pages to be refreshed, even when another page in the site has an error.
Question is, How can I put a piece of code in the error handling block to uncache the current page..
Pseudo code.
try
{
page load
}
catch (Exception ex)
{
// Add C# code to not cache the current page this time.
}
You can simply use HttpResponse.RemoveOutputCacheItem as follows:
try
{
//Page load
}
catch (Exception ex)
{
HttpResponse.RemoveOutputCacheItem("/mypage.aspx");
}
See: Any way to clear/flush/remove OutputCache?
Another way to do that catch exception in Application_Error and use Response.Cache.AddValidationCallback, from this solution:
public void Application_Error(Object sender, EventArgs e) {
...
Response.Cache.AddValidationCallback(
DontCacheCurrentResponse,
null);
...
}
private void DontCacheCurrentResponse(
HttpContext context,
Object data,
ref HttpValidationStatus status) {
status = HttpValidationStatus.IgnoreThisRequest;
}
Related
Base on this questions and the answers there, I like to ask what is the proper way of redirecting.
The default way using the Redirect(url, endResponse) is throw the ThreadAbortException because is called with endResponse=true that calls the End() method and so, if you use it inside a try/catch block, this exception shown there and that can be assumed as error, but actually a user is try to redirect to a page by stopping the rest of the page processing.
The other possible ways is to call the Redirect(url, endResponse) with endResponse=false following by HttpContext.Current.ApplicationInstance.CompleteRequest(); By using that you do not get any exceptions.
So the question is what is better to use and why.
You must call the redirect always with endRespose=true or else any hacker can see whats on the page by simple hold the redirect.
To prove that I use the NoRedirect plugin for Firefox to hold the redirect. Then I test the two cases and here is the results:
I have a simple page with that text inside it
<form id="form1" runat="server">
<div>
I am making a redirect - you must NOT see this text.
</div>
</form>
and then on Page load try to make the redirect with both cases:
First case, using the Complete Request();
try
{
// redirect with false that did not throw exception
Response.Redirect("SecondEndPage.aspx", false);
// complete the Request
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
catch (Exception x)
{
}
and there boom, you can see whats inside the page !
And second case
try
{
// this is throw the ThreadAbortException exception
Response.Redirect("SecondEndPage.aspx", true);
}
catch (ThreadAbortException)
{
// ignore it because we know that come from the redirect
}
catch (Exception x)
{
}
Nothing shown now.
So if you do not like a hacker to see whats on your page, you must call it with endResponse to true and stop what other processing is made -eg return from function and not continue.
If for example you check if the user is authenticated he can see that page or if not he must redirect to login, and even in the login if you try to redirect him with endResponse to false, then holding the redirect the hacker can see - what you believe that can not because you use the Redirect.
My basic point here is to show the security thread that exist if you are not stop to send data back to the browser. The redirect is a header and instruction to the browser, but at the same time you need to stop send any other data, you must stop send any other part of your page.
It is not required to call Response.Redirect with true for endResponse to solve the security issue of outputting the page content after the redirect call. You can accomplish this another way and avoid causing a ThreadAbortException at the same time (which is always bad). Below are snippets of a page I created with 5 buttons that cause redirects in different ways, the RedirectRenderOverride button being the ideal as it is the one that triggers the Render method to do nothing. This has been tested with the NoRedirect add-in. Only two cases avoid outputting anything other than the 302 object moved response - RedirectEnd and RedirectRenderOverride.
Code In Front
<asp:Button ID="Button1" runat="server" OnClick="RedirectCompleteRequest" Text="RedirectCompleteRequest"/>
<asp:Button ID="Button2" runat="server" OnClick="RedirectClear" Text="RedirectClear"/>
<asp:Button ID="Button3" runat="server" OnClick="RedirectRenderOverride" Text="RedirectRenderOverride"/>
<asp:Button ID="Button4" runat="server" OnClick="RedirectEnd" Text="RedirectEnd"/>
<asp:Button ID="Button5" runat="server" OnClick="RedirectEndInTryCatch" Text="RedirectEndInTryCatch"/>
Code Behind
public partial class _Default : Page {
private bool _isTerminating;
protected void RedirectEnd(object sender, EventArgs e) { Response.Redirect("Redirected.aspx"); }
protected void RedirectCompleteRequest(object sender, EventArgs e)
{
Response.Redirect("Redirected.aspx", false);
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
protected void RedirectClear(object sender, EventArgs e)
{
Response.Clear();
Response.Redirect("Redirected.aspx", false);
}
protected void RedirectRenderOverride(object sender, EventArgs e)
{
Response.Redirect("Redirected.aspx", false);
_isTerminating = true;
}
protected void RedirectEndInTryCatch(object sender, EventArgs e)
{
try {
Response.Redirect("Redirected.aspx");
} catch (ThreadAbortException) {
// eat it
} finally {
Response.Write("Still doing stuff!");
}
}
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
{
if (!_isTerminating) {
base.RaisePostBackEvent(sourceControl, eventArgument);
}
}
protected override void Render(HtmlTextWriter writer)
{
if (!_isTerminating) {
base.Render(writer);
}
}
}
Response.End calls Thread.CurrentThread.Abort internally and, according to Eric Lippert, calling Thread.Abort, "is at best indicative of bad design, possibly unreliable, and extremely dangerous."
I have an asp.net web application that performs some license checks before calling up the login page... if the product is not licensed then it navigates to a abc.aspx page with some error details. This license check is an HttpModule which is configured via web.config.
I have an event handler for context authentication. Whenever the abc.aspx page is called, this event is fired multiple times and the page load never happens.
on Init, i use this code to add the event handler
context.AuthenticateRequest += new EventHandler
When i use a html page, this issue does not seem to occur. The issue exists even if i use some other aspx page for example xyz.aspx...
How can stop this authentication to takes place n number of times. I have tried with HttpContext.Current.Response.End(), it stops the infinite calls, but does not load the page, the page appears blank.
Any one has any idea about this issue?
snippet of Global.asax.
<%# Application Language="C#" Inherits="Microsoft.Practices.CompositeWeb.WebClientApplication" %>
<script runat="server">
private static bool _initializedAlready = false;
private static readonly Object s_lock = new Object();
//fires once on asp.net worker process start
protected override void Application_Start(object sender, EventArgs e)
{
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (_initializedAlready)
{
return;
}
lock (s_lock)
{
if (_initializedAlready)
{
return;
}
//custom initialization code
base.Application_Start(sender, e);
_initializedAlready = true;
}
}
public override void Init()
{
base.Init();
//initialize the license module here....
licenseModule.Init(this);
}
</script>
The init() method of license module
public void Init(HttpApplication context)
{
context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);
}
I think the problem is due to the fact that you are authenticating abc.aspx as well.
When you go to a page and the license check fails, it redirects to abc.aspx. Unfortunately, you did not exempt abc.aspx from this check, and it checks itself, and then redirects to itself again and again and again.
What you can do is to only attach the authenticate request event in your Init() method when the page is not "abc.aspx". Something like:
if(!context.Context.Request.RawUrl.Contains("abc.aspx"))
context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);
However, if you only want to do this check on the login page, you would be better off putting the authentication check just on the login page code behind.
I want to display all unhandled excpetions in the appplication using Javascript. For this I have defined onError event inside my custom base class of my pages. Here is the code for my Base Page:
namespace Loan
{
public class BasePage : System.Web.UI.Page
{
public BasePage()
{
}
protected override void OnError(EventArgs e)
{
//Report Error
Exception ex = Server.GetLastError();
if (ex is HttpUnhandledException && ex.InnerException != null)
{
ex = ex.InnerException;
}
var _message = "Error : "+ ex.Message.ToString();
DisplayAlert(_message);
Server.ClearError();
return;
}
protected virtual void DisplayAlert(string message)
{
ClientScript.RegisterStartupScript(
this.GetType(),
Guid.NewGuid().ToString(),
string.Format("alert('{0}');", message.Replace("'", #"\'")),
true
);
}
}
}
The alert is never displayed for an unhandled exception. However, if I call the DisplayAlert from any Page
base.DisplayAlert(ex.Message);
the javascript alert is displayed. How can I get the javascript alert to be displayed for the unhandled exceptions from the base page.Or is there any other way to display these exception messages to the user. I don't want to redirect them to a generic error page as it sends them back and forth.
This is expected. If the exception is unhandled, the OnError event on BasePage will execute and your child page won't continue to execute, there's nothing to render as the BasePage is pure code. If you want to spit out the alert you'd need to write directly to the Response but you should still see a blank page after an unhandled exception occurs.
protected virtual void DisplayAlert(string message)
{
Response.Write(string.Format("<script>alert('{0}');</script>", message.Replace("'", #"\'")));
}
Of course, when you call DisplayAlert directly, it works because you are just calling a method, the Page execution continues normally.
I frankly dislike your approach. You should log the exception and redirect to another page, the typical Oooooooooopsss, me screwed up kind of thing.
I have a large ASPX page with many ASCX controls. If a control throws an exception, it should log the exception and hide only itself. All the other controls should still render.
How do I handle exceptions on individual ASCX's raised from the front-end file (the ASCX and not the code-behind)? for example: a control trying to reference an invalid property using the <%= MethodThatThrowsANullReferenceException() %> syntax.
Obviously using the generic error handler method in Global.asax won't solve the problem. I need to handle exceptions on individual controls.
Make all your UserControls inherit from a custom base class, like such:
public class CustomUserControl : UserControl
{
protected override void Render(HtmlTextWriter writer)
{
try
{
base.Render(writer);
}
catch (Exception e)
{
writer.Write("Could not load control. Sad face.");
}
}
}
I tried overriding Render method but this doesn't cover all exceptions.
For example, if some kind of exception is thrown during Page_Init, Load or Render, this will prevent the page from rendering.
We have different people working on different modules (controls) that can be loaded into a single Page, but I'm not responsible for the quality of the code of each module, so even if it's not best practice, i needed to catch exceptions and identify which control fails to load, because the application can't fail just because one module does.
For this particular scenario that is not so rare nowadays, neither custom, application or page error handling will work well.
The solution I've come up was:
Each Module (Control.ascx) when needs to be loaded into the Page (aspx) , is contained into a ModuleShell that will hold some specific features and will be responsible for helping the Page_Error handling to work properly.
This ModuleShell , instead of trying to trap the exception of its child control that failed, will just monitor in each life cycle stage if it managed to Load properly.
Here's an snippet of it:
protected void Page_Init(object sender, EventArgs e)
{
Modules.CurrentState = _mod;
}
protected void Page_Load(object sender, EventArgs e)
{
Modules.CurrentState = _mod;
}
protected void Page_PreRender(object sender, EventArgs e)
{
Modules.CurrentState = _mod;
}
Modules is a static class used to store session variables.
CurrentState is a variable that ModuleShell use to record their names in.
The Page_Error located in the only aspx we got, will get the last recorded ModuleShell that tried to load. Since any exception will stop page rendering, the last ModuleShell to record its name to the main Page, it's probably the one that failed to load properly.
It's a sloppy solution but it's transparent to the Module Developer.
AFAIK, this is not possible (at least in an easy way).
Rich Custom Error Handling with ASP.NET:
When errors happen, an exception is
raised or thrown. There are three
layers at which you may trap and deal
with an exception: in a
try...catch...finally block, at the
Page level, or at the Application
level. The first two happen right
inside a page's code, and code for
application events is kept inside
global.asax.
The Exception object contains
information about the error, and as
the event bubbles up through the
layers, it is wrapped in further
detail. In rough terms, the
Application_Error exception contains
the Page_Error exception, which
expands on the base Exception, which
triggered the bubbling in the first
place.
If there is an exception occured inside the user control, the only way to catch it inside the user control is to handle it inside a try { } catch { } block.
I think the lowest level when the exception like this could be caught is the next - Page_Error level like this:
protected void Page_Error(object sender, EventArgs e)
{
// the control which throw an exception
var control = (Control)sender;
control.Visible = false;
// the exception itself
var exception = Server.GetLastError();
Context.ClearError();
}
the Context.ClearError() method is even preventing an exception from bubbling up further on to Application_Error. But unfortunatelly, then unhandled exception is thrown the page processing stops and error processing is started instead. This means the render of page will stop too (so you won't see the controls next to that which caused this exception).
You could wrap the method you are trying to call in your own method that will return the same type, but with a try {} catch {} block.
public string MethodWrapper()
{
try
{
return MethodThatCanThrowException();
}
catch (SomeExceptionType)
{
//log exception
return string;
}
}
One option, as suggested by Jim Bolla was to make all controls inherit from the same base class and use a Try/Catch in the Render method. This would have worked. Unfortunately many of the controls I am dealing with already have different base classes.
This solution worked for me:
I added the following code to each user control (I'm sure this can be refactored further to reduce duplication):
#region Error Handling
public event EventHandler ControlCrashed;
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override void RenderChildren(HtmlTextWriter writer)
{
try
{
base.RenderChildren(writer);
}
catch (Exception exc)
{
Logger.Error("Control failed to load. Hiding control. Message: " + exc, exc);
//Ignore and hide the control.
this.Visible = false;
if (ControlCrashed != null)
ControlCrashed(this, EventArgs.Empty);
}
}
#endregion
This catches any front-end rendering problems. The parent page can handle the ControlCrashed event if it wishes to display a nice error message.
I need to test a condition in several ASPX code-behind files and, in some cases, would like to completely bypass the normal page load process so that the corresponding ASPX page is not loaded. Intead, I'd like to send a custom response to the browser that's written from a code-behind method.
Does anyone know where to start- what method(s) in the page lifecycle to override and the best technique to ensure that my custom Response.Write is sent to the browser while the normal ASPX page content is suppressed?
Thanks.
Probably the easiest way to do it - use Page_Load().
protected void Page_Load(object sender, EventArgs e)
{
bool customResponse = true;
if (customResponse)
{
Response.Write("I am sending a custom response");
Response.End(); //this is what keeps it from continuing on...
}
}
The "easy" way to do it, with Response.End() is terrible for performance, throwing an exception which terminates the thread.
http://blogs.msdn.com/b/tmarq/archive/2009/06/25/correct-use-of-system-web-httpresponse-redirect.aspx
http://weblogs.asp.net/hajan/archive/2010/09/26/why-not-to-use-httpresponse-close-and-httpresponse-end.aspx
I had the same question and solved it this way. It's a two-step process: First call HttpApplication.CompleteRequest() and exit your processing. Next override Render() so that the base method is not called. The example code then becomes:
bool customResponse = true;
protected void Page_Load(object sender, EventArgs e)
{
if (customResponse)
{
Response.Write("I am sending a custom response");
this.Context.ApplicationInstance.CompleteRequest();
return; // Bypass normal processing.
}
// Normal processing...
}
protected override void Render(HtmlTextWriter writer)
{
if (!customResponse)
base.Render(writer); // Then write the page as usual.
}
It really depends what you're responding to, is it a posted Form field, authentication info etc...?
The method shown using Page_Load will work, but anything before that point in the page lifecycle will also execute.