XMLHttp /  ASP.NET U.S. Postal Address Validation

by Peter A. Bromberg, Ph.D.

Peter Bromberg
"Abuse of power isn't limited to bad guys in other nations. It happens in our own country if we're not vigilant. Those in power get jaded, deluded, and seduced by power itself." -- Clint Eastwood

Not long ago I went to the US Postal Service's site where they have an online address validation service. If you aren't familiar with this, here is a direct link to their page. They also offer a service where you are supposed to be able to submit addresses in bulk via a WebService call, but you have to apply for it.

On the questionnaire form, in typical bureaucratic fashion, you have to answer some questions, one of which I seem to remember being something about whether it was for commercial use. Now if the rules say that commercial use is disallowed, common sense would dictate that they could simply tell you this up front, and save you and me some of our hard-earned tax dollars, right?

Nope. They actually make you go through the whole process, believing you'll be approved to get a "key" allowing you to use the service, and then, some three days later after some clerk with a sub - room temperature IQ has reviewed your request, you get back an email saying that "Commercial use isn't allowed". Folks, this is the U.S. Government; don't fight it.



So what I did is simply raise the middle finger of my right hand at the screen, calm down, and proceed to write my own web-scraped version of what I needed. The code I'll show you is cross-browser and it's all client-side javascript, and I'll point you to an online version you can try out, but I must warn you that if you are using Firefox, even though this code is 100% Firefox - compatible, it won't work.

The developers of the Firefox browser, in their wisdom, crippled the XMLHTTP Request object to only work with requests to the same domain that the page originated from. In Internet Explorer, as long as the site is in your Trusted Sites list, it is no problem. Firefox, you'll need to write a completely separate server side page in your favorite language, have your XMLHTTP Request make a call to this page (on the same domain), do the XMLHTTP scraping at the server, and return the results to the client page. Actually, you can "digitally sign" your script to get around this, but then it won't be compatible with other browsers. DOH! You call this "standards"?

Personally, I find this approach quite insulting. The Firefox people are essentially saying, "Mr. User, we think you are so dumb that we are gonna prevent you from doing this even if you know what you are doing and want to override the behavior. And that's the end of that, Mr. Firefart Surfer FanBoy." To be fair, IE7 will also have a native (non-ActiveX) implementation of XMLHttpRequest that behaves exactly the same way. However, you can still use ActiveX, as you can see in the sample code below.

In actual fact, the only way you can successfully perform cross-domain remote scripting calls with any browser, without resorting to a server-side "proxy page", is to employ an "on demand" script tag that retrieves JSON (Javascript Object Notation) from the source. A typical implementation of this might look like so:

function msAddScript( url, query, callback)
{
eltScript = document.createElement("script");
eltScript.setAttribute("type", "text/javascript");
if( url.indexOf('?') > -1)
url += '&';
else
url += '?';
url += 'q=' + query +"&callback=" +callback;
eltScript.setAttribute("src", url);
document.getElementsByTagName('head')[0].appendChild(eltScript);
}

The server would return a block of client script that essentially looks like this:

callbackMethod( JSONDataHere );

A discussion of JSON is beyond the scope of this article, but essentially it is a shorthand way of representing objects that when "Eval-ed" in Javascript, translates into an instance of the object. It's not pretty, but it is more compact than XML.

When a script tag is added to the DOM dynamically as in the above, the script is automatically executed, regardless of what domain it was loaded from! This is why the restrictions on XMLHTTPrequest are, in my opinion, so childish. So the callback is automatically invoked with the JSON data as a parameter and everthing is eval-ed and executed, including whatever DOM contructs you have to build your visible results. I should mention also that Jason Diamond's Anthem.Net has a Manager class that already does all this, including creating JSON representations of every possible object including even a DataSet.

The XMLHTTP fiasco is not my only beef with Firefox, it also has a burdeningly strict interpretation of Javascript and DOM that can make writing cross-browser script a royal pain in the butt, but that's another story. Otherwise, its a good browser and I commend them for their efforts. Some people seem to have gotten all religious about this Firefox vs IE thing, personally I couldn't care less. Since we get about 20% of our traffic on Firefox, I guess I have to at least try to make more of my stuff accomodate it's quirks.

(Now you see, the above statement would probably start a religious war from the Firefox afficionados about how terrible Microsoft is and what a miserable browser "Internet Exploder" is, that it's IE which has all the quirks, etc. ad-nauseum) Fact of the matter is, I think Internet Explorer is pretty damn good browser, and IE 7.0 looks like it will be even better. You have to remember that from a security standpoint, if you are number one, you're the guy they all take their pot shots at. Standards are a good thing, but like any other good thing, you can get so carried away that your ultra-strict (and perhaps not entirely accurate) interpretation does more harm than good from a usability standpoint. Devout Penguinistas and FireFartians should keep in mind that inevitably, as their alternative offerings grow and change, so too will the targets of the potshot - takers.


So here is the code, commented (I hope) sufficiently for you to see what it does:

<HTML>

<HEAD>

<TITLE>USPS Address Validation Example</TITLE>

<script>

var url1="http://zip4.usps.com/zip4/zcl_0_results.jsp?visited=1&pagenumber=0&firmname=&address2=";

// 2 Broadway

var url2="&address1=&city="; //NEW YORK

var url3="&state=";//NY

var url4="&urbanization=&zip5=";

 

function getAddress( street, city, state)

{

if(street=="" || city=="" || state =="")

{

 document.getElementById("result").innerText="Please fill in all fields.";

 return;

}

var fullurl=url1+street+url2+city+url3+state+url4;

var x=createXMLHttp();

x.open("GET",fullurl, false);

x.Send(null);

var res=x.responseText;

try{

// strip off everything before where the result starts

var startpos = res.indexOf("<td headers=\"full\"")+124;

res=res.substring(startpos)

if(res.toUpperCase().indexOf("<HTML")>0)

{

document.getElementById('result').innerText="Address Not valid";

return;

}

// strip off everything after the result

var endpos=res.indexOf("</td>")-10;

res=res.substring(0,endpos);

//clean up line breaks

res=res.replace("<br />","");

res=res.replace("<br/>","");

res=res.toUpperCase();

// clean off HTML Spaces

res=res.replace("&NBSP;", " ");

res=res.replace("&NBSP;", " ");

res=res.replace("&NBSP;", " ");

res=res.replace("&NBSP;", " ");

document.getElementById('result').innerText=res;

}

catch(e)

{

document.getElementById('result').innerText="Address Not valid";

}

}

 

//Generic cross-browser XMLHttp object

function createXMLHttp() {

// NOTE Here I am trapping for IE7 to use ActiveXObject since it also now has a native
// XMLHttpRequest object without COM that behaves just like "other browsers"

if (typeof XMLHttpRequest != "undefined" && !window.ActiveXObject) {  

        return new XMLHttpRequest();

    } else if (window.ActiveXObject) {

      var aVersions = [ "MSXML2.XMLHttp.5.0",

        "MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0",

        "MSXML2.XMLHttp","Microsoft.XMLHttp"

      ];

 

      for (var i = 0; i < aVersions.length; i++) {

        try {

            var oXmlHttp = new ActiveXObject(aVersions[i]);

            return oXmlHttp;

        } catch (oError) {

            //Do nothing

        }

      }

    }

    throw new Error("XMLHttp object could be created.");

}

</script>

</HEAD>

<BODY>

<BASEFONT FACE="Tahoma">

<div align="center"><h3>USPS Address Validation</H3></div>

<input type=text id=street value="">Street<BR/>

<input type=text id=city value="">City<BR/>

<input type=text id=state value="">State Abbrev &nbsp;<input type=button onclick="getAddress(document.getElementById('street').value, document.getElementById('city').value, document.getElementById('state').value);" value="GO" /><BR/>

<textarea  id=result style="width:400px;" rows=10></textarea>

</BODY>

So basically, we take the street, city and state of the address you want to validate against the Postal Service database, format them into a GET Url, and use The XmlHTTPRequest object to make the call. The result, which is an HTML Page, comes back in the responseText property on a synchronous call. There's no need to do it asynchronously because its very fast, so a blocking call is the order here. The US Postal service always gets the request through. At least, that's what Al Gore told me.

At that point, I just use simple string manipulation to chop off the junk HTML before and after the results, then clean up extraneous HTML Tags such as <BR /> and &nbsp, and I've got my result for display. If the result comes back with your full USPS - adjusted address including ZIP+4 zipcode, your address is valid. If not, we just display the appropriate error message. Yes, I know -- I could have used Regex, but this job is so simple. And besides, if they change the page, your Regex can break too.

Here's a link to a test page, you can simply View Source and you have everything:

http://www.nullskull.com/articles/USPS.htm

It is relatively easy to massage the above Javascript into a nice .NET Class library. Here is some example code:

using System;

using System.Net;

 

namespace PAB.Utils.USPS

{

    public class AddressValidator

    {

        private  AddressValidator()    {}

 

        public static string ValidateAddress(string street, string city, string state)

        {

string url1=

"http://zip4.usps.com/zip4/zcl_0_results.jsp?visited=
1&pagenumber=0&firmname=&address2="; // 2 Broadway

            string url2="&address1=&city="; //NEW YORK

            string url3="&state=";//NY

            string url4="&urbanization=&zip5=";   

            if(street=="" || city=="" || state =="")

            {               

            throw new ArgumentException("missing or invalid parameter.", "street:city:state");

            }

            string fullurl=url1+street+url2+city+url3+state+url4;

            WebClient cln = new WebClient();

            try

            {

                string res=System.Text.Encoding.UTF8.GetString(cln.DownloadData(fullurl));       

                // strip off everything before where the result starts

                int startpos = res.IndexOf("<td headers=\"full\"")+124;

                res=res.Substring(startpos);

                    if(res.ToUpper().IndexOf("<HTML")>0)

                    {                       

                        return "Invalid Address";

                    }

                // strip off everything after the result

                int endpos=res.IndexOf("</td>")-10;

                res=res.Substring(0,endpos);

                //clean up line breaks

                res=res.Replace("<br />","");

                res=res.Replace("<br/>","");

                res=res.ToUpper();

                // clean off HTML Spaces

                res=res.Replace("&NBSP;", " ");

                res=res.Replace("&NBSP;", " ");

                res=res.Replace("&NBSP;", " ");

                res=res.Replace("&NBSP;", " ");

                return res;

            }

            catch(Exception ex)

            {

                return ex.Message;

            }

        }

    }

}

I've put the above, along with a nice ASPX web page to test it, into a Visual Studio.NET 2003 Solution that you can download and try right away:

Download the code that accompanies this article

You can thank the Postal Service for their incredible efficiency. If you are a Firefox user, please note that I've taken care to ensure that this page looks the same in Firefox as it does in Internet Explorer. I even spend some time fixing up the "Search" Button at the upper right of our pages to allow you to use the enter key instead of having to click the submit button, just as it already functioned for IE users. It was originally only about 3 lines of Javascript code for IE, and now it's about 20 lines of code, but its "cross browser". So there. I wish you great peace and happiness with either Firefart or Internet Exploder. May all your methods return, etc.


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.
Article Discussion: