Monday, April 20, 2009

.NET FileUpload control throws a javascript error in IE when a non-existent filename is entered

I ran into an interesting bug with .NET/IE the other day and didn't readily find a comprehensive solution, so I thought I'd post what I came up with to hopefully guide some poor soul home. I've tried to pare my code down to the bare basics so that it can be modified to your specific circumstance. This is specifically for IE7. Other browsers (Chrome, Firefox, Safari) don't exhibit this behavior and should be handled differently.

My solution involves using client side scripting to validate the existence of a valid filename in the control, and halting the submission of the form in the case of a poorly formed filename (permitting a blank control). I've written it in such a way as to permit multiple FileUpload controls on the page all subscribing to the same validation mechanism.

-Registering the controls to be validated.
-Validating said controls
-Capturing a PostBack action
-Ignoring other browsers

Registering the controls to be validated

Since the listeners and function pointers (more on these later) should only appear once, I created an array to house the FileUpload controls of interest. It's the responsibility of the control to register.


/*this should go in your main script section, outside of the browser checking area*/

var FileUploadControls = new Array();
function RegisterFileUploadControls(fcobj)
{
FileUploadControls[FileUploadControls.length] = fcobj;
}




/*this can appear at any later point in the document, because of the way my controls would render, I had them register at the same time that they render. */

RegisterFileUploadControls(document.getElementById("fileupload_clientid"));



Validating the FileUploadControls

I opted to use a regular expression style to validate the format of whatever is in the textbox of the fileupload control. Write whatever test you feel is appropriate. I kept it relatively basic, most of the errors that I was getting from users were people putting in garbage in the textbox. There's additional error checking on the server side for the actual receipt of a file, but that's beyond the scope of this post.


//put this in your main script area, inside the browser checking

function CheckFileUploadControls()
{

/*boolean that will return false if the text doesn't pass the test.
a false return value is intended to halt submission of the form, true will permit the sumission. */
bFileUploadValidation=true;

/*put in a regular expression that fits the amount of validation that you're looking for, this one is
just looking for an alpha character, max two, followed by a “:” and “\” and whatever comes
after is permitted. Pretty loose validation, but you can get as fancy as you'd like. */

pAlphaLower = new RegExp("^([a-z][A-Z]){1,2}:\\.*");

//loop through the controls that have registered

for(x=0;x {
oElement=FileUploadControls[x];
oMessage=FileUploadControlsMessages[x];

/*we're only interested in elements that have specified a value. Blank fileuploads
are permitted. */

if(oElement.value!='')
{
//regex test against the value
if(!pAlphaLower.test(oElement.value))
{
//insert your error messaging here//
bFileUploadValidation=false;
}
}
}
//otherwise permit the form submission to continue
return bFileUploadValidation;
}



Capturing a PostBack action

Traditionally, a postback is going to be initiated from the Form in one of two ways, a “submit” button that invokes standard HTML behavior, or a call to __doPostBack which invokes the behavior through something like document.forms[n].submit();

The standard HTML behavior can be approached by adding an event listener to the FORM object. IE does this differently than the other browsers and this code will cause an error outside of IE (google: javascript addEventListener for examples).


//put this in your main script area, inside the browser checking
function AttachFormListner()
{
document.forms[0].attachEvent('onsubmit',CheckFileUploadControls);
}



The second most common way that ASP.NET is going to raise a postback is by calling their javascript function __doPostBack(obj,args). What I did was create a function pointer to Microsoft's version of __doPostBack, then overrode __doPostBack with my error handling finishing with a call to the base class.



/*insert this code in your main script area, inside the browser checking
this is the function pointer to the standard version of the method */
var base_post_back = __doPostBack;

/*here I create another function pointer that overrides the standard version
the function just calls our validation routine and aborts the form submission in the case of a
filename that doesn't match our rule. insert this code in your main script area */

__doPostBack = function(obj, objj){if(CheckFileUploadControls()){base_post_back(obj,objj);}}



Ignoring other browsers

The best way to sift the browsers is based on the javascript functionality rather than getting into the mess of versions. If I can't use “attachEvent” on a FORM object, then I'm not interested in continuing further with the code.


//this should surround all the code that is subject to browser checking

if(document.forms[0].attachEvent){
//all those code snippets should go in here
};



Hope this was a help, feel free to use the comments to talk w/me.

No comments: