Handling Request Validation 'silently' - asp.net

I'm trying to override the onError event handler of a web form to allow "A potentially dangerous Request.Form value was detected from the client" type errors to be handled within the form rather than ending up at the application level error handler.
I found some sample code like this :
protected override void OnError(EventArgs e)
{
// At this point we have information about the error
HttpContext ctx = HttpContext.Current;
Exception exception = ctx.Server.GetLastError();
string errorInfo =
"<br>Offending URL: " + ctx.Request.Url.ToString() +
"<br>Source: " + exception.Source +
"<br>Message: " + exception.Message +
"<br>Stack trace: " + exception.StackTrace;
ctx.Response.Write(errorInfo);
// --------------------------------------------------
// To let the page finish running we clear the error
// --------------------------------------------------
ctx.Server.ClearError();
base.OnError(e);
}
Which satisfactorily catches the error and writes an error message out to the screen but what I really want to do is to be aware of the error when Page_Load fires and so be able to display a 'normal' error message on the webform.
I'm sure there's a good way to do this but I don't know it ! Suggestions ?
(BTW for various reason I don't want to turn off the checking at either form or app level and neither do I wish to rely on Javascript - thanks)

You actually can catch the error at the page level, but it will kill the page lifecycle. So you have to use a trick to get around it. Example:
public override void ProcessRequest(HttpContext context)
{
try
{
base.ProcessRequest(context);
}
catch(HttpRequestValidationException ex)
{
context.Response.Redirect("HandleValidationError.aspx");
}
}
HandleValidationError.aspx can be anything, including a redirection back to the same page (perhaps with a querystring with information regarding the error, e.g. "ContactForm.aspx?error=Invalid+Request")

I think I understand what you want to do, but I'm afraid it might be impossible. When your ASP.NET page performs a postback, a new thread is created on the server to handle the request. Before your page lifecycle even has a chance to begin, the offending XSS is found and an exception is thrown. Once this exception is thrown, you are "evicted" from the ASP.NET page lifecycle and there is no way to re-enter it. At this point, the only thing you can do on the client side is output the error, or redirect to an error page.
What you seem to want to do is catch the exception, write it out somewhere on the page, and continue with the ASP.NET page lifecycle (i.e. restoring the control tree, restoring viewstate, invoking event handlers, etc). The problem is once an unhandled exception is thrown you no longer have access to the ASP.NET page lifecycle. In this particular case, there is nowhere to put a try/catch block because the exception is thrown internally from the ASP.NET lifecycle before your own code is called.
I know you said you don't want to rely on Javascript, but in this case I think using Javascript is the only way to get the behavior you want. You can still keep server-side validation, just in case your users disable Javascript or type some input that your Javascript doesn't handle.

I don't think you'll be able to handle the error in the Page_load event. In the ASP.NET Page Life cycle validation events occur after the page loads.
Maybe you can add a hidden div (<asp:Panel Visible=false ...) that contains your "normal error message". if the OnError event fires display the error message div.
jason

Related

Proper way to skip page execution after Response.RedirectToRoute

I'm writing an asp.net 4.5 application using the new routing features. I have a page that displays some information about an item. In the Page_Load event I check the route data (item id) and user permissions, and if something isn't right (e.g. the id is for a deleted item) I use Response.RedirectToRoute to send them packing, right back to the home page. Do not pass GO, do not collect $200.
This made perfect sense until I tried to access a deleted item and instead of the home page I got an error page. I did some digging and discovered that even after I use RedirectToRoute (unlike the standard Redirect method) the rest of the page code continues to execute, which at the very least seems wasteful (since I'm just going to throw away the results) and throws errors when the necessary data doesn't exist.
I did a little more SO mining and discovered the incredible evil that is Response.End(). It does what I need, but even the MSDN page tells me that Response.End is the bastard child of an ancient accursed language and isn't fit to see the light of day. The primary objection seems to be the fact that Response.End throws an exception, and that's bad for performance. I'm not the most experienced developer, so I don't understand the issue entirely, but I have trouble believing that throwing an exception is more expensive than loading the entire web page. The workarounds seem rather complex and excessive for a task so simple, especially since most pages require some kind of validity check.
What am I supposed to do in this situation? Use Response.End and beg forgiveness for my insolence? Cobble together some ugly workaround? Or is my perspective on the problem all wrong to begin with? I'd really like to know.
Update: Now that I've thought it over a bit more, I wonder if I do have the wrong perspective on the problem. Perhaps an immediate redirect is the not the best response for the user experience. Would I be better off wrapping all the controls in a panel, and using something like this?
Private Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init
'Validation Code
If notValid Then
ControlsPanel.Visible = false
ErrorPanel.Visible = true
End If
End Sub
RedirectToRoute is actually wraps Response.Redirect passing false for ending the request - hence, the request continues. You can use HttpApplication.CompleteRequest as immediate call to terminate the request so that next application events would not be invoked.
Response.End (and other Redirect variation) throws ThreadAbortException to abort the request processing thread which is really a bad way to stop request processing. In .NET world, exception processing is always considered expensive because CLR then needs to search up the stack all the way up for exception processing blocks, create stack trace etc. IMO, CompleteRequest was introduced in .NET 1.1 to avoid the same which actually relies on setting flag in ASP.NET infrastructure code to skip further processing except EndRequest event.
Yet another (and better) way is to use Server.Transfer and avoid client round-trip for setting redirect all together. Only issue is that client would not see the redirected URL in the browser address bar. I typically prefer this method.
EDIT
CompleteRequest wouldn't never work in page case where subsequent page events would be still invoked because page being a handler, all its events happens within a single (and current) application event ProcessRequest. So only way seems to be setting a flag and check that flag in overrides such as Render, PreRender, RaisePostBackEvent etc.
From maintenance perspective, it make sense to have such functionality in base page class (i.e. maintaining the flag, offering CompleteRequest method to subclasses and overriding life cycle event methods). For example,
internal class PageBase: System.Web.UI.Page
{
bool _requestCompleted;
protected void CompleteRequest()
{
Context.ApplicationInstance.CompleteRequest();
_requestCompleted = true;
}
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl,
string eventArgument)
{
if (_requestCompleted) return;
base.RaisePostBackEvent(sourceControl, eventArgument);
}
protected internal override void Render(HtmlTextWriter writer)
{
if (_requestCompleted) return;
base.Render(writer);
}
protected internal override void OnPreRender(EventArgs e)
{
if (_requestCompleted) return;
base.OnPreRender(e);
}
... and so on
}
I may be going out on a limb by not answering the question directly, but I liked seeing your update regarding user experience. I prefer your suggested approach.
I like to give a 410 error for id's that are not valid and extend it a bit with (translated from C#):
Protected Sub ItemDoesNotExist()
'item does not exist, serve up error page
ControlsPanel.Visible = False
ErrorPanel.Visible = True
'add meta tags for noindex
Dim mymeta As New HtmlMeta()
mymeta.Name = "robots"
mymeta.Content = "noindex"
Page.Header.Controls.Add(mymeta)
'RESPOND WITH A 410
Response.StatusCode = 410
Response.Status = "410 Gone"
Response.StatusDescription = "Gone"
Response.TrySkipIisCustomErrors = True
'important for IIS7, otherwise the Custom error page for 404 shows.
Page.Title = "item gone"
End Sub

If an exception is caught on the server side of an update panel, can you see the stack trace somehow?

I'm trying to debug a third party library that is throwing an null reference exception in certain cases, but I am only getting the exception from the javascript side, which doesn't contain the stack trace in the error message.
Is there a value in the scriptmanager or something similar with more information about the exception, or should I be removing the update panels to get at the raw exception?
(Easier said then done, its nested several update panels deep)
Thanks!
Look into ScriptManager.AsyncPostBackError event.
You can do something like this
protected void ScriptManager1_AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
//do something with the exception
log(e.Exception.Message);
// show a message to the user.
ScriptManager1.AsyncPostBackErrorMessage =
"An error occurred." + e.Exception.Message;
}
}
You can turn off ajax features temporary by setting EnablePartialRendering property of the ScriptManager to false.

How to tell if Page_PreRender has run?

I'm running a method in an overridden Page OnUnload, but only if the Page_PreRender method has run.
Obviously, I can flip a class-level bool when I'm in Page_PreRender and check it in OnUnload, but if there's a more intrinsic way to tell is Page_PreRender has run, I'd like to use that.
Any ideas?
Thanks for any thoughts.
UPDATE: Let me rephrase my question slightly. I'm looking for the answer to whether there is a simple way, inherent in the Page life cycle, perhaps a property that is set by the ASP.Net frameowork, perhaps something else, that is different after Page_PreRender has run versus when Page_PreRender has not run.
I am currently setting a boolean in Page_PreRender to tell me if it has run. It works, but I don't like this solution if there is a way to accomplish the same thing without adding the extra boolean check. Creating an event that fires during Page_PreRender is the same level of redundancy I'd like to avoid, if possible.
You mention (in your comments on another post) that your problem manifests itself when calling Response.Redirect() because it throws a ThreadAbortException, which leads to your OnPreRender() event not being called. So why not use this instead?:
Response.Redirect("~/SomePage.aspx", false);
The "false" you see there indicates if execution of the page should terminate right there and then. By default, Response.Redirect() uses "true". If you need your OnPreRender() event to run so that your OnLoad() event will have everything it needs, then set it to "false" and just make sure you either jump to the end of your Page_Load() after calling Response.Redirect() or that the code that would execute after it is fine to run.
Maybe you don't like the idea of passing "false" using the overloaded Response.Redirect() method so that's why you didn't go that route. Here is some documentation that may help sway your mind:
Microsoft states that "passing false for the endResponse parameter is recommended" because specifying "true" calls the HttpResponse.End() method for the original request, which then throws a ThreadAbortException when it completes. Microsoft goes on to say that "this exception has a detrimental effect on Web application performance". See here in the "Remarks" section: http://msdn.microsoft.com/en-us/library/a8wa7sdt.aspx
This was posted last year on MSDN:
The End method is also on my “never
use” list. The best way to stop the
request is to call
HttpApplication.CompleteRequest. The
End method is only there because we
tried to be compatible with classic
ASP when 1.0 was released. Classic
ASP has a Response.End method that
terminates processing of the ASP
script. To mimic this behavior,
ASP.NET’s End method tries to raise a
ThreadAbortException. If this is
successful, the calling thread will be
aborted (very expensive, not good for
performance) and the pipeline will
jump ahead to the EndRequest event.
The ThreadAbortException, if
successful, of course means that the
thread unwinds before it can call any
more code, so calling End means you
won’t be calling any code after that.
If the End method is not able to raise
a ThreadAbortException, it will
instead flush the response bytes to
the client, but it does this
synchronously which is really bad for
performance, and when the user code
after End is done executing, the
pipeline jumps ahead to the EndRequest
notification. Writing bytes to the
client is a very expensive operation,
especially if the client is halfway
around the world and using a 56k
modem, so it is best to send the bytes
asynchronously, which is what we do
when the request ends the normal way.
Flushing synchronously is really bad.
So to summarize, you shouldn’t use
End, but using CompleteRequest is
perfectly fine. The documentation for
End should state that CompleteRequest
is a better way to skip ahead to the
EndRequest notification and complete
the request.
I added this line after calling Response.Redirect(), as MSDN suggests, and noticed everything appeared to run the same. Not sure if it's needed with 4.0, but I don't think it hurts:
HttpContext.Current.ApplicationInstance.CompleteRequest();
Update 1
Using "false" in the call to Response.Redirect() avoids the ThreadAbortException, but what about other Unhandled Exceptions that could be thrown on your page? Those exceptions will still cause your problem of OnUnload() being called without OnPreRender(). You can use a flag in OnPreRender() as everyone suggests to avoid this, but if you're throwing Unhandled Exceptions, you've got bigger problems and should be redirecting to an error page anyway. Since Unhandled Exceptions aren't something you plan to throw on every postback, it would be better if you wrapped your OnUnload() logic in a Try-Catch. If you're logging and monitoring your exceptions you will see that an Unhandled Exception was thrown right before logging a NullReference Exception in the OnUnload() event and will know which one to ignore. Because your OnUnload() will have a Try-Catch, it will safely continue processing the rest of the page so you can Redirect to the error page as expected.
Update 2
You should still have your OnUnload() wrapped in a Try-Catch, but I think this is what you're really looking for (remember IsRequestBeingRedirected will be true when calling Response.Redirect or when redirecting to an error page after an Unhandled Exception).:
if (HttpContext.Current.Response.IsRequestBeingRedirected != true)
{
//You're custom OnUnload() logic here.
}
With this, you will know if it is safe (or even worth it) to process your custom logic in the OnUnload() event. I realize I should have probably lead off with this, but I think we learned a lot today. ;)
NOTE: The use of Server.Transfer() will also call the dreaded Response.End(). To avoid this, use Server.Execute() with the preserveForm attribute set to "false" instead:
Server.Execute("~/SomePage.aspx", false);
return;
NOTE: The thing about Server.Execute("~/SomePage.aspx", false); is that IsRequestBeingRedirected will be false, but your OnPreRender() will still execute, so no worries there.
The answer is Yes, you can, but not always :)
According the Reflection code, the ScriptManager class contains the private bool field _preRenderCompleted, which is set to true while handling internal IPage interface PagePreRenderComplete event.
You can use the Reflection to get this field from ScriptManager.GetCurrent(page) resulting object
I am not sure what exactly you mean by this. According to the ASP.NET Page Lifecycle PreRender always runs before Unload. If you perform some if condition inside this PreRender event and you would like to test in the Unload whether the condition was satisfied a boolean field on the page class seems a good idea.
Add trace=true to the page directive.
Set a boolean field in the PreRender event handler, then check if it was set in the Unload event handler.
Create a custom event that fires in the PreRender event.
I don't think there is any sort of state stored because the ASP.NET engine does not really need that, as it knows its state implicitely.
Searching with .NET Reflector, it seems the page render events are raised from this internal System.Web.UI.Page method:
private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint)
You can have a look at it, there is no notion of state. The only information you can get is the trace. If you have access to the Unload event, then you should have access to the trace? or I miss something :-)
Since the trace is in fact a dataset undercovers (see my answer here: Logging the data in Trace.axd to a text/xml file), you maybe could get the information. But
setting trace=true is not recommended in production though...

opening an attachment in <ASP: UpdatePanel throw this error

*Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.
Details: Error parsing near '�PNG
'.*
if i remove the <asp:updatepanel its all working fine
protected void gvFiles_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Download")
{
byte[] byteArray = item.AttachContent.ToArray();
Response.Clear();
Response.AppendHeader("Content-Disposition", "attachment; filename=\"" + item.AttachFileName + "\"");
Response.AppendHeader("Content-Length", byteArray.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(byteArray);
Response.End();
}
}
This is occurring because you are trying to do a Response.BinaryWrite.
You get this error because because you are trying to return non HTML to the UpdatePanel using an asynchronous callback which is not allowed. The only thing I can suggest is to try using a PostBackTrigger and targetting a control outside of your UpdatePanel.
The best solution would be to make you download button spawn another window that calls an .ashx (generic handler) and server up the binary via the ashx. You can just attach a javascript function to handle the click which will open a new window to the ashx with whatever params are needed passed in.
This link has more information on the exact problem and ways of solving it.
There are several ways to fix this
problem each fix has specific scops.
1.Register ButtonDownload PostBackTrigger control in Triggers
child tag of the update panel as shown
below.
2.You can also use RegisterPostBackControl method of the
ScriptManager control in Page_Load as
shown below.
As the error describes, Response.BinaryWrite is among the blacklisted activities during an asynchronous callback. This is due to the UpdatePanel's special handling of the content returned during its asynchronous callback.
I would recommend moving your attachment-download logic into a separate page, HTTP handler, or service, allow it to be accessed with a GET (by controlling parameters in the query string), and changing the element in gvFiles from whatever it is (Button, ListButton, ImageButton, etc) into a hyperlink to the new resource. By downloading on a GET from a separate resource, you will avoid the error.

Redirecting ASP.NET Page

A word to be said. This is the best .NET community ever. Keep it up !
I had a problem lately that I managed to have for it 2 solutions and I am wondering which one should I choose.
Here the issue :
When a user requests my site ( www..com), I am gonna redirect him to (www..com/en) because it is a multilingual site.
I am using .NET routing to to that.
In the Page_Load of the default.aspx page I check if the language in the Routing Collection is available, I don't redirect. If it is not available I redirect to (www.___.com/en). Here is the code :
if (Page.RouteData.Values.Count == 0)
{
if (SessionManager.IsUserAuthenticated)
{
//Redirect To User HomePage for his Main Language
Page.Response.Redirect(UserManager.GetUserMainPageWhenLoggedIn(SessionManager.LoggedUser.LanguageID,true));
}
else
{
Page.Response.Redirect(String.Format("~/{0}", Cultures.en.ToString()), true);
Helpers.SetCulture(Cultures.en.ToString());
}
}
I am using Response.Redirect to do that. Now if I set to End The Response the method parameter, it will throw an exception so I can handle it throught
try
{
this.InitializeLayout();
}
catch (ThreadAbortException ex)
{
}
catch (Exception ex)
{
ExceptionManager.LogException(ex);
}
If I don't end the Response, the page will execute the whole lifecyle, redirect and then do it again which results a double execution of the page.
My main objective is not to execute the page 2 times to minimize processing ( if the site gets hammered by a big traffic). If I end the Response, a ThreadAbortExeption will be thrown and I think this is not good for the site.( I can catch it and not log it).
What do u think guys?
You could avoid throwing the exception by, as you said, passing false to the endResponse parameter. If you want to then avoid executing the page, you could also call HttpContext.Current.ApplicationInstance.CompleteRequest(); which should skip the rest of the code on your page.
Note, however, that postback and render methods will still execute. If you want to avoid that, you must explicitly include code to ignore them. A great example of how to do this by overriding the RaisePostBackEvent and Render methods is here: --> http://www.c6software.com/articles/ThreadAbortException.aspx
You can use the Application_BeginRequest event handler in global.asax file or a HttpHandler to do your redirections. In both cases you will avoid 2 calls being made.
I would also suggest that you use server.Transfer instead of response.redirect.

Resources