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.