![]() |
< Day Day Up > |
![]() |
18.5. Using Web Services with Complex Data TypesThe BirthDayWS Web Service used throughout this chapter accepts integers as input and returns a string value. This is useful for introducing Web Service principles because HTTP GET, HTTP POST, and SOAP all support it. However, Web Services also have the capability of serving up more complex data types such as data sets, hash tables, images, and custom objects. Before data can be sent to or from a Web Service, it is serialized using XML serialization. Conversely, it is deserialized on the receiving end so it can be restored to its original type. As we saw in Chapter 4, "Working with Objects in C#," not all data can be serialized. Thus, when designing a Web Service, it is important to understand restrictions that apply to serialization:
In this section, we work with two Web Service examples that illustrate the use of complex data. Our first example creates a Web Service that accepts the name of an image and returns it as a byte stream. The second example creates a client to use the Amazon Web Services provided by Amazon.com, Inc. These services offer a rich梑ut practical梥ampling of accessing multiple Web methods and processing a wide variety of custom classes. A Web Service to Return ImagesImage manipulation usually requires representing an image as a Bitmap object. However, because bitmaps cannot be serialized and transferred directly, we must find an indirect way to transport an image. The not-so-difficult solution is to break the image into bytes and return a byte stream to the client, who is responsible for transforming the stream to an image. The logic on the server side is straightforward: a FileStream is opened and associated with the image file. Its contents are read into memory and converted to a byte array using tempStream.ToArray() This byte array is then sent to the Web client (see Listing 18-6). Listing 18-6. Web Service to Return an Image as a String of Bytes
<%@ WebService Language="C#" Class="WSImages" %>
using System;
using System.Web.Services;
using System.IO;
using System.Web.Services.Protocols;
public class WSImages: System.Web.Services.WebService {
[WebMethod(Description="Request an Image")]
public byte[] GetImage(string imgName) {
byte[] imgArray;
imgArray = getBinaryFile("c:\\"+imgName+".gif");
if (imgArray.Length <2)
{
throw new SoapException(
"Could not open image on server.",
SoapException.ServerFaultCode);
} else
{
return(imgArray);
}
}
public byte[] getBinaryFile(string filename)
{
if(File.Exists(filename)) {
try {
FileStream s = File.OpenRead(filename);
return ConvertStreamToByteBuffer(s);
}
catch(Exception e)
{
return new byte[0];
}
} else { return new byte[0]; }
}
// Write image to memory as a stream of bytes
public byte[] ConvertStreamToByteBuffer(Stream imgStream) {
int imgByte;
MemoryStream tempStream = new MemoryStream();
while((imgByte=imgStream.ReadByte())!=-1) {
tempStream.WriteByte(((byte)imgByte));
}
return tempStream.ToArray(); // Convert to array of bytes
}
}
Our client code receives the byte stream representing an image and reassembles it into a Bitmap object. Because the Bitmap constructor accepts a stream type, we convert the byte array to a MemoryStream and pass it to the constructor. It can now be manipulated as an image.
WSImages myImage = new WSImages();
try {
// Request an image from the Web Service
byte[] image = myImage.GetImage("stanwyck");
MemoryStream memStream = new MemoryStream(image);
Console.WriteLine(memStream.Length);
// Convert memory stream to a Bitmap
Bitmap bm = new Bitmap(memStream);
// Save image returned to local disk
bm.Save("c:\\bstanwyck.jpg",
System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
Using Amazon Web ServicesTo use the Amazon E-Commerce Service, you must register for a developer's token, which is required as part of all requests made to the Web Services. In addition, you should download (http://www.amazon.com/webservices) the developer's kit that contains the latest documentation, examples, and梞ost importantly梐 WSDL file defining all the services. An examination of the WSDL file reveals that AmazonSearchService is the Web Service class that contains the numerous search methods available to clients. These methods provide the ability to search the Amazon product database by keyword, author, artist, ISBN number, manufacturer, actor, and a number of other criteria. Each search method takes a search object as an argument that describes the request to the server and returns a ProductInfo object. For example, a request to search by keywords looks like this: AmazonSearchService amazon = new AmazonSearchService(); KeywordRequest kwRequest = new KeywordRequest(); // Set fields for kwRequest ProductInfo products = amazon.KeywordSearchRequest(kwRequest); Sending a Request with the AmazonSearchService ClassTable 18-3 contains a sampling of the methods available for searching Amazon products. These methods are for accessing the Web Service synchronously. An asynchronous form of each method is also available that can be accessed using the techniques discussed earlier in this chapter.
Each call to a Web method passes an object that describes the search request. This object is different for each method梖or example, AuthorSearchRequest requires an AuthorRequest object, whereas KeyWordSearchRequest requires an instance of the KeywordRequest class. These classes expose almost identical fields. Each contains a unique string field that represents the search query, five other required fields common to each class, and some optional fields for sorting or specifying a locale. Table 18-4 lists unique and shared fields for each method listed in Table 18-3.
Using the ProductInfo Class to Process the Web Service ResponseThe Web Service responds to the search request with a ProductInfo object containing results from the search. This object exposes three important fields: a TotalResults string contains the number of products retrieved by the request, a TotalPages string that indicates how many pages these results are displayed in, and the important Details array that contains a detailed description of products that constitute one returned page of results. This array is of the Details type. Table 18-5 shows the fields that are related to books.
This is only a small sample of the fields available in the Details class. There is a particularly rich set of fields worth exploring that define video products. Creating a Proxy for the Amazon Web ServicesOur first step is to create a proxy class from the Amazon WSDL information. The downloadable kit includes a WSDL file, and it can also be retrieved from the Internet, as we do here. Using the VS.NET command line, we place the proxy source code in the file AZProxy.cs. wsdl.exe /out:c:\client\AZProxy.cs http://soap.amazon.com/schema3/AmazonWebServices.wsdl Next, we create an assembly, AZProxy.dll, containing the proxy that will be used by client code. It is linked to assemblies containing .NET Web classes required by the application. csc/t:library /r:system.web.services.dll /r:system.xml.dll AZProxy.cs You can make a quick test of the service using this barebones application, azclient.cs: using System; using System.Web.Services; namespace webclient.example { public class AmazonClient { static void Main(string[] args) { // Search for books matching keyword "butterflies" AmazonSearchService amazon = new AmazonSearchService(); KeywordRequest kwRequest = new KeywordRequest(); kwRequest.keyword = "butterflies"; kwRequest.type = "heavy"; kwRequest.devtag= "*************"; // your developer token kwRequest.mode = "books"; // search books only kwRequest.tag = "webservices-20"; kwRequest.page = "1"; // return first page ProductInfo products = amazon.KeywordSearchRequest(kwRequest); Console.WriteLine(products.TotalResults); // Results count } } } Compile and execute this from the command line: csc /r:AZProxy.dll azclient.cs azclient When azclient.exe is executed, it should print the number of matching results. Building a WinForms Web Service ClientLet's design a Windows Forms application that permits a user to perform searches on books using multiple search options. Open VS.NET and select a Windows application. After this is open, we need to add a reference to the proxy assembly AZProxy.dll. From the menu, select Project ?Add Reference ?Browse. Click the assembly when it is located and then click OK to add it as a reference. You also need to add a reference to System.Web.Services.dll, which contains the required Web Service namespaces. The purpose of the application is to permit a user to search the Amazon book database by keyword, author, or title. The search can be on a single field on a combination of fields. Figure 18-7 shows the interface for entering the search values and viewing the results. The Search buttons submit a search request based on the value in their corresponding text box. The Power Search button creates a query that logically "ands" any values in the text boxes and submits it. Figure 18-7. Overview of how a client accesses a Web Service![]() A single page of results is displayed in a ListView control. Beneath the control are buttons that can be used to navigate backward and forward through the results pages. Each Search button has a Click event handler that calls a method to create an appropriate request object and send it to the Amazon Web Service. A successful call returns a ProductInfo object containing information about up to 10 books meeting the search criteria. Listing 18-7 displays code that creates an AuthorRequest object, sends it to the Web Service, and calls FillListView to display the results in the ListView control. Listing 18-7. Client Code to Display Results of Author Search?tt>azwsclient.cs
// Fields having class-wide scope
int CurrPg; // Current page being displayed
string SearchMode = ""; // Current search mode
int MaxPages =1; // Number of pages available
// This method is called when author Search button is clicked
private bool AuthorReq()
{
AmazonSearchService amazon = new AmazonSearchService();
AuthorRequest auRequest = new AuthorRequest();
auRequest.author = textBox2.Text; // Get author from GUI
auRequest.type = "heavy";
auRequest.devtag= "****KLMJFLGV9"; // Developer token
auRequest.mode = "books";
auRequest.tag = "webservices-20";
auRequest.page = CurrPg.ToString();
try
{
// Call Web Service with author query
ProductInfo products =
amazon.AuthorSearchRequest(auRequest);
FillListView(products);
return(true);
}
catch (SoapException ex)
{
MessageBox.Show(ex.Message);
return(false);
}
}
private void FillListView(ProductInfo products)
listView1.Items.Clear(); // Remove current entries
label6.Text=""; // Clear any title
label4.Text = products.TotalResults;
label5.Text = CurrPg.ToString()+" of "+products.TotalPages;
{
MaxPages = Convert.ToInt32(products.TotalPages);
ListViewItem rowItem;
string auth,rev;
for (int i=0; i< products.Details.Length; i++)
{
rowItem = new
ListViewItem(products.Details[i].ProductName);
// Add Author. Make sure author exists.
object ob = products.Details[i].Authors;
if (ob != null) auth =
products.Details[i].Authors[0]; else auth="None";
rowItem.SubItems.Add(auth);
// Add Price
rowItem.SubItems.Add(products.Details[i].OurPrice);
// Add Average Rating
ob = products.Details[i].Reviews;
if (ob != null) rev =
products.Details[i].Reviews.AvgCustomerRating;
else rev="None";
rowItem.SubItems.Add(rev);
// Add Date Published
rowItem.SubItems.Add(
products.Details[i].ReleaseDate);
listView1.Items.Add(rowItem);
}
}
The keyword, title, and power searches use an identical approach: Each has a routine comparable to AuthorReq that creates its own request object. The only significant difference pertains to the power search that creates a Boolean query from the search field values. The format for this type query is field:value AND field2:value AND field3:value. For example: "author:hemingway AND keywords:Kilimanjaro" This application was designed as a Windows Forms application. It could just as easily be set up as Web page under ASP.NET. The code to reference the assembly AZProxy.dll is identical. The ListView control is not supported on a Web Form, but you could easily substitute a DataGrid for it. |
![]() |
< Day Day Up > |
![]() |