Share via


Handling ValidateRequest errors within a Page

 I have seen a couple posts asking how to process the errors caused by the ValidateRequest feature in ASP.NET 1.1.  People want to know how to trap the error within a single page and do something with it rather than let the error bubble up to the Application_OnError event.

When ValidateRequest fails, it emits an HTTP status of 500. The actual HTTP response looks something like:

 HTTP/1.1 500 Internal Server Error
Date: Mon, 07 Jul 2003 18:45:36 GMT
Server: Microsoft-IIS/6.0
MicrosoftOfficeWebServer: 5.0_Pub
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 4701

<head> 
   <title>A potentially dangerous Request.Form value was detected from the client (TextBox1=&quot;&lt;foo&gt;&quot;).</title> 
. 
. 
. 

Using the web.config File

ASP.NET will interrogate the HTTP status code for the response and redirect to the specified page in the web.config file.  You could create a custom error page to display information for requests having status code 500 by using the customErrors section of the web.config file. 

<customErrors mode ="On" >

 <error statusCode ="500" redirect ="Oops.aspx" />

 </customErrors>

The shortcoming of this technique is that it will redirect all responses with HTTP status 500, not just the ones arising from the ValidateRequest attribute of the Page directive.

Handling Exceptions in global.asax and Application_Error

You can handle the exception by putting code in the global.asax file's Application_OnError method. This allows you to at least inject different handling for an HttpRequestValidationException than for other types of exceptions. 

protected void Application_Error(Object sender, EventArgs e){   System.Exception oops = Server.GetLastError();   if(oops.GetBaseException() is System.Web.HttpRequestValidationException )   {     System.Diagnostics.Debug.Assert(false);   }}

To set page delgates and other handling information, the typical model is to use the OnInit() method of your code-behind class to insert initalization code or put code in the Page_Load method body. If you try to set the Page.ErrorPage property in either of these typical methods, you will see that it has no effect on handling errors arising from the ValidateRequest page directive in ASP.NET 1.1.  If you want to handle things at the Page level, you have to look into how the validation occurs and how exceptions are handled in ASP.NET to see how to address it.

Setting the Page.ErrorPage Property 

The Page object does not process the validation directly. Rather, the get accessors of the HttpRequest object for the Forms, QueryString, and Cookies collections are modified to call the validateNameValueCollection() or ValidateCookieCollection() methods if the ValidateRequest attribute of the Page directive is true. This process is detailed in a post on Victor Garcia Aprea's web log. We can use this information to tap into the validation processing and handle it on a page basis.

Think back to how constructors act in .NET.  When an object is instantiated, its constructors will be called if any are present.  If your class does not provide a constructor, the compiler will insert a default constructor (a public constructor with no parameters) for you. This default constructor will implicitly call the default constructor of the class that it derives from; in this case, the default constructor for the System.Web.UI.Page object will be called.

The default constructor for the Page object will retrieve the HttpRequest collections mentioned earlier, causing validation on them if ValidateRequest is set to true. This is why simply setting a handler for the Page.Error event or setting the Page.ErrorPage property in the OnInit or Page_Load methods of your code-behind class will have no effect: The System.Web.UI.Page object throws an HttpValidationRequest error before InitializeComponent is ever called for your class.

You have to get in the Control Lifecycle early enough to override this behavior.  We already attempted to insert code into the OnInit method, which is the first method in the Control Lifecycle. That means that we have to get into the lifecycle prior to the OnInit method.  The earliest point to provide handling for the error on a per-page basis if you are using the Page.ErrorPage property is to set the property in an explicitly declared default constructor:

public WebForm8():base(){   this.ErrorPage = "oops1.aspx";}

Overriding the OnError Method

Instead of redirecting to another page, you might want to handle the validation logic directly in your page, maybe displaying a friendly error message.  Just as we did when handling the error in the global.asax file's Application_OnError method, we can catch the exception at the Page level and check the type of exception.  We do this by overriding the OnError method of the Page class.  The OnError method is provided via the TemplateControl class that the Page object derives from.  Overriding this method allows you to handle the handle without worrying about the default constructor, you just add the following to the Visual Studio.NET generated code-behind class:

protected override void OnError(EventArgs e){      System.Exception oops = Server.GetLastError();      if(oops.GetBaseException() is System.Web.HttpRequestValidationException )      {        System.Diagnostics.Debug.Assert(false);        Response.Write(oops.ToString());        Response.StatusCode = 200;        Response.End();              }``       }

Note that this code explicitly sets the StatusCode of the HttpResponse to "200". This is the HTTP status code for "OK, nothing went wrong." We also have to make sure to call Response.End(). The HttpServerUtility does not expose a method or property to set the last error. If the page goes out of scope and the HttpServerUtility.GetLastError does not return "null", then the Application_OnError method is fired and the exception handling pipeline continues.

Comments

  • Anonymous
    July 31, 2003
    q

  • Anonymous
    July 31, 2003
    Add the ValidateRequest="false" attribute in the @Page directive to stop the validation from being done in the first place.

  • Anonymous
    July 31, 2003
    If you turn it off, you still have to validate all of the posted data for potentially malicious input. By simply checking for the exception, you let ASP.NET do the heavy lifting while lightening your page's load for validation controls.

  • Anonymous
    September 20, 2003
    sdfsdfsd

  • Anonymous
    November 05, 2003
    <A>

  • Anonymous
    November 13, 2003
    Is there a way to add the error text to the page without erasing what was already on the page?

  • Anonymous
    December 15, 2003
    <Script>alert("yoyo")</script>

  • Anonymous
    December 16, 2003
    Sadly enough this trick of moving up in the Page lifecycle does not work for errors generated by posts that exceed the <httpRuntime maxRequestSize>. You still get "The page cannot be displayed", even if ending the HttpResponse, or when setting the StatusCode.

    I've tried pretty much any combination of the following code lines:
    Context.ClearError();
    Response.Redirect("Error.aspx", false /true/);
    Response.StatusCode = 200;
    Response.End();

    Would you know of a solution for this problem?

  • Anonymous
    July 06, 2004
    sdfd

  • Anonymous
    July 16, 2004
    The comment has been removed

  • Anonymous
    July 16, 2004
    You can turn off the request validation using ValidateRequest="false", but then the rest of the controls on that page are vulnerable to attack. Obviously, this is a bad thing to do.

    A better design is to override the OnError event and handle the exception.

  • Anonymous
    June 18, 2007
    How's that for a blog-post title? There are a couple cool things shown within this post, highlighting

  • Anonymous
    June 18, 2007
    PingBack from http://msdnrss.thecoderblogs.com/2007/06/18/creating-a-general-purpose-asynchronous-soap-client-in-aspnet-20/