Beginning Visual Basic 2005 (2006)
.pdf
Chapter 20
Service to a production environment. When the test interface starts, it displays the methods flagged to be exposed on the server. When you click through to the page tied to a specific method, the test interface presents a form that you can use to invoke it.
When the method is invoked, to the method it “feels” just like a normal call — in other words, there’s nothing special about writing Web Services, and everything that you’ve learned so far still applies.
You already know that Web Services are powered by SOAP. When you click the Invoke button, the SOAP message that’s returned to the caller (in this case, your Internet Explorer) contains the response. You can see that this is indeed the value you returned from the method buried within a block of XML:
<?xml version=”1.0” encoding=”utf-8” ?>
<string xmlns=”http://tempuri.org/”>Hello World</string>
The structure of the XML that makes up the SOAP message, by and large, is not important. However, when you’re working through more examples, we’ll point out where the actual results can be found.
Adding More Methods
Let us build some methods that illustrate your Web Service actually doing something. In this next Try It Out exercise, you’ll be adding a Web method that will calculate the square root of the number that you pass into it. You’ll be adding the Web method and writing the code to calculate the square root, as well as testing this new Web method.
Try It Out |
Adding a SquareRoot Method |
1.Open the Code Editor for Service.asmx. Add this new method to the Service class below the existing HelloWorld method:
Public Function GetSquareRoot(ByVal number As Double) As Double
Return Math.Sqrt(number)
End Function
If you can’t type into the code window, it means that the instance of Internet Explorer that Visual Studio 2005 opened is still running. Close down the test interface windows and any extra windows displaying the SOAP responses, and the project should stop running. Alternatively, select Debug Stop Debugging from the menu.
2.Run the project. You’ll notice that the new method does not appear in the list at the top of the page. In fact, you will see the same screen that was shown previously. This is due to the fact that you didn’t mark the method with the WebMethod attribute. I did this to show you that a class can contain methods that, although public, are not exposed on the Web Service. Close the browser and add the WebMethod attribute:
<WebMethod()> _
Public Function GetSquareRoot(ByVal number As Double) As Double Return Math.Sqrt(number)
End Function
656
Web Services and .NET Remoting
3.Run the project again and you should see the new method at the top of the page.
4.To see the correct error message for this example, you may have to change a setting in your browser. Make sure you uncheck “Show friendly HTTP error messages” under the Advanced tab from the Tools Internet Options menu in Internet Explorer.
5.Click the GetSquareRoot link. This time, the Invoke form should offer a way to enter a number because of the WebMethod parameter. Without entering a number, click Invoke.
6.When the new browser appears, you won’t see a SOAP response; instead you’ll see something that looks like this:
System.ArgumentException: Cannot convert to System.Double.
Parameter name: type ---> System.FormatException: Input string was not in a correct format.
You’ll see this kind of message whenever you enter invalid information into the Invoke form. In this case, it’s telling us that it cannot convert to System.Double, which should be a big giveaway that it can’t convert an empty string to a floating-point value.
7.Close the browser window and enter 2 into the number field. Click Invoke and you’ll get this response:
<?xml version=”1.0” encoding=”utf-8” ?>
<double xmlns=”http://tempuri.org/”>1.4142135623730952</double>
How It Works
If you look in the SOAP message that was returned, you’ll find a double value that’s as close as you can get to the square root of 2.
<?xml version=”1.0” encoding=”utf-8” ?>
<double xmlns=”http://tempuri.org/”>1.4142135623730952</double>
So you know that the method works. You should have also seen by now that building simple Web Services is not hard. This was Microsoft’s intent with the Web Services support in .NET — the plumbing to build a service is remarkably easy. Everything you’ve learned about creating classes, building methods with parameters, and returning values is paying dividends here, because there’s virtually no learning curve to climb. You can concentrate on building the logic behind the Web Service, which, after all, is the bit you get paid to do!
The Picture Ser ver Ser vice
Because building simple Web Services is so straightforward, you’ll move on relatively quickly to building a proper application that does something practical with a Web Service. You’ll also look at building a desktop client application that uses the Web Service, because up to now all you’ve used is the test interface provided by the WebService class. The specific example you’ll use will be to build a Web Service that allows an application to view pictures placed on a remote server.
657
Chapter 20
Creating the Project
In this section, you will:
Set up a folder on your Web Service server (this could be your local machine or a remote machine where the Web Service will run) that contains pictures downloaded from a digital camera. You’ll divide this folder into subfolders for different events, for example, “Deborah’s Graduation,” “Trip to Boston,” and so on.
Build a Web Service that can interrogate the folder to return a list of subfolders. You’ll also be able to return a list of the files in each subfolder.
When you do return a file, also return details on the file, such as graphic format, width, height, and so on.
Set up the Web site so that you can view the pictures you find using a Web browser.
That doesn’t sound like anything you can’t do using an ASP.NET Web site. However, what you can do with the Web Service that you cannot do with a standard Web site is build your own custom front-end Windows Forms application. With a Web site, you are tied to using HTML and a Web browser to present the information on the server to the user.
Try It Out |
Creating the Project |
1.Select File New Web Site from the menu to create a new ASP.NET Web Service project and call it PictureService. You can place the Web Service anywhere you want on your hard drive. Using the built in Web server with Visual Studio 2005 you do not have to worry about IIS integration during development.
2.When the project loads, you don’t want to use the default Service.asmx or Service.vb files. As extra practice, you will delete the default Web Service files and add them back. Using the Solution Explorer right-click both Service.asmx and Service.vb and select Delete. Click OK when asked.
3.Using the Solution Explorer again, right-click the PictureService project. Select Add New Item. In the Add New Item dialog box, choose the Web Service template. Enter the name of the service as Service and click Add. The Solution Explorer should now contain Service.asmx.
4.Now that you’ve created this new .asmx page, when you run the project, you want this to be the one that gets loaded into Internet Explorer. In the Solution Explorer, right-click the Service.asmx entry and select Set as Start Page.
5.To make the pictures available over the Web site, you need to create a folder called Pictures, directly within the folder out of which the Web Service itself runs. Right click the project in Solution Explorer and choose Add Folder Regular Folder. Name the folder Pictures.
6.Now, right-click Service.asmx in the Solution Explorer and select the View Code menu item. Find the HelloWorld method again and alter the code so that it looks like this:
Public Class Service
Inherits System.Web.Services.WebService
Public Sub Service
658
Web Services and .NET Remoting
End Sub
<WebMethod()> _
Public Function HelloWorld() As String
Return Server.MapPath(Context.Request.ServerVariables.Item(“script_name”))
End Function
End Class
7.Run the project. As usual, when Internet Explorer starts, click the HelloWorld method link. Click the Invoke button when the page loads. TheWeb Service should now tell you the full path of Service.asmx:
<?xml version=”1.0” encoding=”utf-8” ?>
<string xmlns=”http://tempuri.org/”>c:\inetpub\wwwroot\PictureService\Service.asmx </string>
8.You can see that, on the computer on which this exercise was run, the folder containing Service.asmx is c:\inetpub\wwwroot\PictureService\. You’ll refer to this folder as the service root folder from this point on. Now, open a copy of Windows Explorer (the local file explorer, not Internet Explorer). Go to the service root folder, and you will see the new subfolder you created called Pictures, as shown in Figure 20-3.
Figure 20-3
9.Now you’ll need to find some pictures to use with the service. You can use any picture you like as long as they are in either GIF or JPEG format.
10.Divide the pictures into a set of three subfolders under the Pictures folder. Use any folder name that you like for your three subfolders. In this example, the folders are Beach, Mountains, and Remodel.
659
Chapter 20
How It Works
At this point, you should have both a Web Service and a load of pictures that you can use with the service. In a moment, you’ll start building methods on the service that are able to return the folders to the user.
The only piece of code you wrote in this section was the code that returned the complete path of
Service.asmx:
<WebMethod()> _
Public Function HelloWorld() As String
Return Server.MapPath(Context.Request.ServerVariables.Item(“script_name”)) End Function
This is quite an advanced ASP.NET trick (Web Services are, after all, based on ASP.NET technology) and is beyond the scope of the book. However, what we can tell you is that all pages running on ASP.NET are able to make many determinations about their environment, including the physical path in which the server is located.
For more information on building Web sites with ASP.NET 2.0, check out ASP.NET 2.0 Beta Preview (ISBN: 0-7645-7286-5).
Returning Arrays
In the first part of this chapter, you looked at a Web Service that returned single values from each method call, such as a string or a number.
In some cases, you want to return arrays of information. This is particularly true when you ask the Web Service for a list of the picture subfolders. In the next Try It Out, you return an array of string values, each one containing the name of a folder.
Try It Out |
Returning a List of Picture Subfolders |
1.Open the code editor for Service.asmx again. Delete the HelloWorld method.
2.You need a reference to the System.IO namespace for this exercise, so go to the top of the code listing and add this new namespace reference:
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.IO
When you’re building methods, you’re going to need a way to get the full path to your Pictures folder. This uses the work you did before to find the service root folder, but with an extra bit of code to actually get the full path to the Pictures folder. Add this property to
Service:
‘PictureFolderPath - read-only property to return the picture
‘folder...
Public ReadOnly Property PictureFolderPath() As String
660
Web Services and .NET Remoting
Get
‘ get the full path of this asmx page...
Dim strAsmxPath As String, strPicturePath As String strAsmxPath = My.Request.PhysicalPath.ToString()
‘get the service path - everything up to and including
‘the “\”
Dim strServicePath As String = _
strAsmxPath.Substring(0, strAsmxPath.LastIndexOf(“\”) + 1) ‘ append the word “Pictures” to the end of the path...
strPicturePath = strServicePath & “Pictures” |
‘ return the path... |
Return strPicturePath |
|
End Get |
|
End Property |
|
3.Having the name of the folder is just half the battle. In order to do anything useful, you need an object that lets you search through the folder looking for subfolders. System.IO.DirectoryInfo is the class for such an object, so add this property to Service:
‘PictureFolder - property to the DirectoryInfo containing
‘the pictures...
Public ReadOnly Property PictureFolder() As DirectoryInfo
Get
Return New DirectoryInfo(PictureFolderPath)
End Get
End Property
4.Now you can actually build the GetPictureFolders Web method:
‘GetPictureFolders - return an array of the picture folders...
<WebMethod(Description:=”Return an array of the picture folders”)> _ Public Function GetPictureFolders() As String()
‘get hold of the picture folder...
Dim pictureFolder As DirectoryInfo = Me.PictureFolder
‘ get the array of subfolders...
Dim objPictureSubFolder() As DirectoryInfo = _
pictureFolder.GetDirectories()
‘ create a string array to accommodate the names...
Dim arrFolderNames(objPictureSubFolder.Length - 1) As String
‘ now, loop through the folders...
Dim pictureSubFolder As DirectoryInfo, intIndex As Integer
For Each pictureSubFolder In objPictureSubFolder
‘ add the name...
arrFolderNames(intIndex) = pictureSubFolder.Name
‘ next...
intIndex += 1
Next
‘ finally, return the list of names...
Return arrFolderNames
End Function
5.Run the project. When Internet Explorer appears, click the GetPictureFolders link. When prompted, click Invoke, and you should see something similar to the following. Of course, the folders returned will be the folders that you created:
661
Chapter 20
<?xml version=”1.0” encoding=”utf-8” ?>
<ArrayOfString xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns=”http://tempuri.org/”>
<string>Beach</string>
<string>Mountains</string>
<string>Remodel</string>
</ArrayOfString>
How It Works
You can see by the results that you do, indeed, have an array of strings returned. You know this because first, you have the ArrayOfString tag appearing in the string, and second, you actually have the three strings for your three folders.
<?xml version=”1.0” encoding=”utf-8” ?>
<ArrayOfString xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns=”http://tempuri.org/”>
<string>Beach</string>
<string>Mountains</string>
<string>Remodel</string>
</ArrayOfString>
The PictureFolderPath property is quite important, because you’ll frequently use this in your methods. The first thing you do is to get hold of the complete path to the Service.asmx file that’s powering the service:
‘PictureFolderPath - read-only property to return the picture
‘folder...
Public ReadOnly Property PictureFolderPath() As String Get
‘ get the full path of this asmx page...
Dim strStrAsmxPath As String, strPicturePath As String strStrAsmxPath = My.Request.PhysicalPath.ToString()
However, this string will return something like this:
C:\WebSites\PictureService\Service.asmx
And what you ultimately want is this:
C:\WebSites\PictureService\Pictures
Therefore, you have to clip off the Service.asmx at the end and replace it with Pictures. To do this, you use the Substring method of the String class to extract just the portion of the path that you need and return it in the strServicePath variable. The substring that you want to extract starts at position 0 of the string and ends with the last backslash in the path. Using the LastIntIndexOf method of the String class you can easily find the position of the last backslash in the path. You then add 1 to that position to ensure that the last backslash is included in the string returned:
‘get the service path - everything up to and including
‘the “\”
Dim strServicePath As String = _
strStrAsmxPath.Substring(0, strStrAsmxPath.LastIntIndexOf(“\”) + 1)
662
Web Services and .NET Remoting
Next, you want to append the Pictures folder to the strServicePath variable and return the complete path from the property:
‘append the word “Pictures” to the end of the path...
strPicturePath = strServicePath & “Pictures”
‘return the path...
Return strPicturePath
End Get
End Property
System.IO.DirectoryInfo is a class that can help you learn more about a folder on the computer or the network. You create a new property called PictureFolder that returns a DirectoryInfo object based on the value returned by the PictureFolderPath property:
‘PictureFolder - property to the DirectoryInfo containing
‘the pictures...
Public ReadOnly Property PictureFolder() As DirectoryInfo
Get
Return New DirectoryInfo(PictureFolderPath)
End Get
End Property
Once you have that, you can create your GetPictureFolders method. Notice that you have included a description for your WebMethod attribute. This description is displayed on your Service page under the GetPictureFolders link, and it helps to document the Web methods available to be consumed. The first thing this Web method does is use the PictureFolder property to get hold of the DirectoryInfo object that points to C:\WebSites\PictureService\Pictures. You have to use the Me keyword, because you have a local variable with the same name. This removes the ambiguity of the call and makes sure that when you ask for PictureFolder, you actually go off to find the value of the property, rather than returning the current value of pictureFolder, which would be an empty string:
‘ GetPictureFolders - return an array of the picture folders...
<WebMethod(Description:=”Return an array of the picture folders”)> _
Public Function GetPictureFolders() As String()
‘ get hold of the picture folder...
Dim pictureFolder As DirectoryInfo = Me.PictureFolder
The GetDirectories method will return an array of DirectoryInfo objects, one for each of the subfolders:
‘ get the array of subfolders...
Dim objPictureSubFolder() As DirectoryInfo = _ pictureFolder.GetDirectories()
Once you have this array, you can use its Length property to determine how many subfolders the Pictures folder actually has. You can then use this folder to create an empty array of the correct length:
‘ create a string array to accommodate the names...
Dim arrFolderNames(objPictureSubFolder.Length - 1) As String
With the array in place, you can loop through the objPictureSubFolder array and copy the name of each folder into the arrFolderNames array:
663
Chapter 20
‘ now, loop through the folders...
Dim objPictureSubFolder As DirectoryInfo, intIndex As Integer
For Each objPictureSubFolder In pictureSubFolders
‘ add the name...
arrFolderNames(intIndex) = objPictureSubFolder.Name
‘ next...
intIndex += 1
Next
Finally, you can return the array back to the caller:
‘ finally, return the list of names...
Return arrFolderNames End Function
You might have noticed the inconsistency in naming between “directories” and “folders.” With the introduction of Windows 95, Microsoft decided that directories, as they had been called for over a decade, should now be called folders. The group in charge of the DirectoryInfo class in the .NET team, however, apparently believed that “directory” was a better name than “folder.” If you noticed, you’ve always called folders “folders” and the .NET Framework has always called folders “directories.” Providing that each party sticks to its own convention, things shouldn’t get confusing.
Returning Complex Information
So far, whenever you’ve returned anything from a Web Service, you’ve returned only simple values, although you now know how to return arrays of simple values. With a little work, however, you can return complex structures of information from the Web Service.
In this section, you want to return a list of the pictures that are contained within each folder. With the picture subfolders you needed only to know the name, but now for each picture you would like to return the following information:
The filename of the picture (for example, PIC00001.jpg)
The complete URL that points to the picture (for example, C:\WebSites PictureService/Pictures/Beach/PIC00001.jpg)
The name of the folder that contains the picture (for example, Beach)
The size of the image (for example 26,775 bytes)
The date the image was created (for example, 6/26/2002)
The format of the image (for example, JPG)
Try It Out |
Returning Complex Information |
1.To return a set of information, you need to create a structure that you can populate with the information you want. To do this, add a new class to the project by right-clicking on the App_Code directory in the Solution Explorer and selecting Add New Item and then selecting Class. You must add the class to the App_Code directory. Call it PictureInfo. You want to create a structure rather than a class (although in this particular case either will do), so change Class and End Class to Structure and End Structure and add these members:
664
Web Services and .NET Remoting
Public Structure PictureInfo
‘ members...
Public Name As String
Public Url As String
Public FolderName As String
Public FileSize As Long
Public FileDate As Date
Public ImageFormat As String
End Structure
2.To get the pictures contained within a folder, you’ll create a new Web method that takes the name of the folder as a parameter. Open the code editor for Service.asmx and add this code:
‘GetPicturesInFolder - return an array of pictures from the folder...
<WebMethod(Description:=”Return an array of pictures from the folder”)> _ Public Function GetPicturesInFolder(ByVal folderName _
As String) As PictureInfo()
‘ get hold of the folder that we want...
Dim pictureSubFolder As DirectoryInfo pictureSubFolder = _
New DirectoryInfo(PictureFolderPath & “\” & folderName)
‘we need to get the URL of the picture folder...
Dim pictureFolderUrl As String
pictureFolderUrl = My.Request.ServerVariables(“URL”)
‘manipulate the URL to return an absolute URL to the Pictures folder pictureFolderUrl = “http://” & _
My.Request.ServerVariables(“SERVER_NAME”) & “:” & _ My.Request.ServerVariables(“SERVER_PORT”) & “/” & _ pictureFolderUrl.Substring(0, pictureFolderUrl.LastIndexOf(“/”) + 1) & _ “Pictures”
‘get the list of files in the subfolder...
Dim pictureFiles() As FileInfo = pictureSubFolder.GetFiles ‘ create somewhere to put the picture infos...
Dim pictureList(pictureFiles.Length - 1) As PictureInfo ‘ loop through each picture...
Dim pictureFile As FileInfo, intIndex As Integer For Each pictureFile In pictureFiles
‘create a new pictureinfo object...
Dim pictureInfo As New PictureInfo() pictureInfo.Name = pictureFile.Name pictureInfo.FolderName = folderName pictureInfo.Url = pictureFolderUrl & “/” & _ folderName & “/” & pictureFile.Name pictureInfo.FileSize = pictureFile.Length pictureInfo.FileDate = pictureFile.LastWriteTime pictureInfo.ImageFormat = _ pictureFile.Extension.Substring(1).ToUpper
‘add it to the array...
pictureList(intIndex) = pictureInfo intIndex += 1
Next
‘ return the list of pictures...
Return pictureList End Function
665
