2002-09-12 10:08
Using ASP.NET/WebServices For UPS Shipping
Quotes
By Dan Anderson
I like to experiment with new technology, to the point where I have crashed my machine more times than I can remember! But probably the most exciting new technology for me has to be the new .NET framework from Microsoft. (If you are unfamiliar with ASP.NET, the ASP side of the .NET framework, be sure to check out the ASP.NET Article Index!) With so many new capabilities, the possibilities for developers are limited only to the imagination (this IS, of course, my own humble opinion). After reading an article from Richard Lowe entitled Using Microsoft's XMLHTTP Object To Get Data From Other Web Pages, I decided to see how his example could be suited to fit the .NET framework using Web Services.
So, if you're new to ASP.NET, you might be asking what Web Services are, exactly. In short, they are pieces of program logic that are programmatically available via the Internet. Basically, you can author a web service and make its properties and methods available to other developers across the web without writing vast amounts of documentation for an API or distributing DLLs to everyone who wishes to use them. With Web Services, a developer could include functionality from another server into their own code quite easily.
Perhaps one of the coolest features of Web Services is that they can be modified, re-compiled, and made available "on the fly." That means you don't have to stop the web server; that means you don't have to use regsvr32.exe, or interrupt existing calls to the old code! Calls to a Web Service will complete their execution under the old code, while new requests to the web service are handled with the new code, seamlessly. What a deal, eh?
So how do you make use of Web Services? Web services are written as .asmx files and compiled through a supplied application (WebServiceUtil.exe) to create the final web service DLL. (Of course you will need to have the .NET Framework SDK installed... learn more on how to do this at: www.ASP.NET!) Part of this process includes generating SDL (Service Definition Language), which is, essentially, a SOAP description of how the web service works, what parameters are required (including their data types), and return parameters. You can view the SDL output of a web service simply by typing in the full URL of the web service and adding ?SDL to the end of it. For example, the SDL output of our example in this article, using http://localhost/asp2/ups.asmx?SDL as the URL, can be seen on the right.
Using this SDL contract, as it is called, the next version of Visual Studio will read all of the properties and methods of the web service and make them available programmatically to the developer. Pretty neat, eh?
In Richard Lowe's articles, he explains how to use Microsoft's XMLHTTP object to retrieve shipping rate quotes from UPS for packages without visiting the UPS site. After experimenting with his code, I decided to port it to the .NET architecture as a web service because it could prove extremely useful as a tool for understanding how web services will simply the life of developers.
For this article I created a Web Service called "UPS" (not much imagination, eh?), which we will walk through one step at a time. The complete code is a bit lengthy, so you can view it here in its entirity if you'd like. Now, let's walk through the code!
<%@ WebService language="VB" class="UPS" %>
This line says that we are writing a web service, using Visual Basic as the language, and assigning it a class name of UPS. Class names are important, because they define wrappers for various pieces of functionality. The next several lines, each of which begins with the word Imports define the libraries that our web service will require at compilation time in order to include tools required for the web service. In this instance, the two most important ones are System.Net and System.Web.Services, which make the HTTP-POST and HTTP-GET functionality available. We also include Microsoft.VisualBasic, because we are using string manipulation routines and other VB-specific calls within the code.
Next, we declare the class name again, wrapping it around the functionality of the web service. Now we're ready to add our functionality. We declare a public function titled GetPrice, but you will note that we preceded the name of the function itself with a WebMethod() statement. This tells the compiler that the function itself is available via the web. Any function or subroutine which does not include this directive is not accessible from the web, or in other words is not "web-callable".
The GetPrice function requires a number of parameters in order to obtain rate quotes. We will not go into an explanation of these parameters. Instead, you can find out more by going to UPS.com and clicking on the "E-Commerce" link.
Next, we declare several object variables:
Dim sReturn as string =
""
Dim WebReq as WebRequest
Dim WebResp as WebResponse
Dim strStream As
StreamReader
Dim URLRequest as String
The WebReq object variable will be used to hold the request being sent to UPS, while WebResp will hold the response from the UPS server. StrStream is a stream object used to stream the response received into a variable. URLRequest will hold the constructed string to be sent to UPS.
We next call a private function included in the web service called BuildUPSRequest, which builds the querystring from the variables passed in to the GetPrice function. This string is then used with the next line:
WebReq = WebRequestFactory.Create(URLRequest)
This directive creates an instance of the WebRequest object using the WebRequestFactory object, which creates an HTTP request to the specified URL. By default, WebRequestFactory creates requests using the GET method, although you can override that setting through code.
Next, we read the response from the URL called using the following code:
WebResp = WebReq.GetResponse()
strStream = new StreamReader(WebResp.GetResponseStream(),
_
System.Text.Encoding.ASCII)
The first line retrieves the response from the URL using the GetResponse() directive of the WebRequest object, while the second line assigns the output of the response to a stream object. Note that the last directive in the line states System.Text.Encoding.ASCII, which means that we want the returned data encoded as ASCII character text. There are a number of other encoding schemes available, but that is another topic altogether.
Now, we get to use the new Try...Catch mechanism of Visual Basic, which allows us to manage errors more gracefully. In the Try portion of the code, we are reading the returned data in the Stream object a line at a time, looking for the line which contains the relevant portion of data returned by UPS. The line that has this data begins with upsonline, and all of the information regarding the shipping rates in included in that same line, delimited with percent signs. So, we will ignore everything that does not begin with upsonline. Once we have found that line, we use the VB Split function to put the data into an array, which is defined by the line Dim strArrayData() As String. This defines the array as an array of strings.
We check the fourth member of the new array, the first four digits of which indicate success or failure of the request. If the first four digits are not equal to zeros, UPS has returned an error to us. So, we put the error string value into the return parameter of the function and exit the process. Otherwise, if the return code is equal to zeros, the data was successfully retrieved from UPS. The shipping rate in that case would be contained in the thirteenth member of the array, which is then placed in the return parameter of the function.
The Catch portion of the code simply says to put the error code into the return parameter of the function. You could expand on this by writing the error to the Windows Event Log (yes, you can now do this easily in .NET!), but we kept it simple in this example. (In case you're interested in learning how to write to the Event Log with ASP.NET, check out: Writing to the Event Log with ASP.NET!)
That pretty much ends the description of the Web Service file.
Now that we have our Web Service, we need an easy way to call it from an ASP.NET
page. We'll look at how to accomplish this in Part 2!
In Part 1 we looked at
how to create a Web Service. In this part we'll examine how to call that Web
Service via an ASP.NET page!
To call a Web Service from an ASP.NET page we need to create a proxy class DLL. This proxy class wraps all of the complex calls to a Web Service into a simple call that we can make from an ASP.NET page. Creating such a proxy class DLL is fairly simple (although a bit cryptic). It can be accomplished with a batch file:
webserviceutil /c:proxy /pa:http://localhost/asp2/ups.asmx?sdl
/l:VB /n:nsUPS
vbc /out:bin\ups.dll /t:library /r:system.data.dll
/r:system.web.services.dll
/r:system.net.dll
/r:system.xml.serialization.dll ups.vb
The first line uses the WebServiceUtil.exe utility included in the .NET
SDK. Basically, it creates a .VB file containing compiler pre-code, including
references to all the required libraries and functions. The /c:proxy command
indicates that we want to create a stub or proxy class of the web service from
an SDL document. The SDL document is generated by the next portion of the
command /pa:http://localhost/asp2/ups.asmx?sdl. The /pa: portion is the URI path
to the web class file itself.
The /l:VB portion of the line simply says that the web service language used is Visual Basic. Finally, the /n:nsUPS gives the namespace to be used to refer to the web service. The namespace is similar to the "References" option used in Visual Basic. It declares the chunk of code functionality we wish to employ in a page or application.
The second line of the batch file, starting with vbc, is what actually generates the final web service DLL. First, vbc means Visual Basic Compiler. The next piece /out:bin\ups.dll defines the name of the dll we want generated, as well as the directory path to place it in. The /t:library defines the type of output we want the compiler to generate, which, in this case, is a DLL library. Next, the /r: directives ensure that required support libraries are included in the compilation process, much as we declared them via the Imports commands in the .asmx file itself. Finally, we define the source file to be compiled, which in this case is the ups.vb generated by the WebServiceUtil.exe utility.
It is generally recommended that you create a bin directory off of your site/application's root and place any web service dlls you create there. The PDC SDK version of ASP.NET gets a little picky and might, in some instances, require the Bin directory for dlls. There, we've compiled our web class. Now, let's use it!
We won't go into the entire file, because there is too much ground to cover. Instead, we're going to concern ourselves with the portion that involves the use of our new web service. The header of the client file looks like this:
<%@ Import Namespace="nsUPS" %>
Here, we have used the Import directive to load our new web service, using the namespace we defined for it when we compiled it. This creates an internal reference to our new web service. Down the page, in the ShowRate subroutine, you will see the following:
Dim D As New UPS
Dim
PriceData=D.GetPrice(cSvcLevel,cRateChart,cShipperZIP,
_
cReceiverZIP,"US",cPkgWeight,cResidentialInd,
_
cCODInd,cSatPickupInd,cSatDelivInd,cPkgType)
Here, we have declared D as a new instance of our web service, after which we call the function in the web service (GetPrice), passing in the required parameters. The PriceData variable now holds the output of the web service, which is then displayed at the bottom of the page form. Our page, when first loaded, looks like this:
After entering the required information, it calls the web service, which updates the page to look like this:
Note that underneath the "Show Rate" button we now have the price for shipping the package provided from UPS. The files for this example are included below. If you have the PDC SDK version of .NET installed, give them a try!
Happy Programming!