Showing posts with label Web Services. Show all posts
Showing posts with label Web Services. Show all posts

Monday, December 6, 2010

Calling secured Web services methods from PHP

  • An overview of SOAP in PHP
  • A demonstration of how to create security headers with an actual implementation example
  • A description of complex structures that may be passed as parameters to SOAP methods and how to transform the PHP representation of such parameters into the required format of a SOAP call argument

  •  Getting started
    Open source applications and open standards are becoming the preferred method for building applications on the Web. The most effective way to create such applications is to utilize existing software components and services across the Web. A common implementation of such a tiered application includes server-side PHP scripts using Web services. With this type of architecture, there is an increased need for secured communication.
    SOAP and PHP - Overview and references
    Before the introduction of PHP 5, it was hard to call Web services in pure PHP. In PHP 5, the application developer has a number of options for implementing PHP Web services clients: PEAR:SOAP, NuSOAP, and the new SOAP extension. This tutorial focuses on the use of the latter.
    The SOAP extension has improved capabilities over previous PHP solutions, including the SoapVar type and several OO mechanisms that can be used to construct virtually any complex SOAP type.
    According to Rosenberg and Remy's 2005 book (see Resources), "WS-Security is an overarching conceptual model that abstracts different security technologies into "claims" and "tokens"...SOAP headers are used for directive information. This is essentially the place where SOAP security lives. System-level information used to manage and secure the message is placed here as well. SOAP runtimes and intermediaries process SOAP header directives. Headers are intended to add new features and functionality and WS-Security headers will be located here. A sender can require that the receiver understand the header. Headers speak directly to the SOAP processors and can require that a processor reject the entire SOAP message if it does not understand the header. If a header contains critical security information that the SOAP processor does not understand, you may not want it to process this SOAP message at all."
    The developerWorks article Access an enterprise application from PHP script (see Resources) describes the usage of SOAP in PHP 5 for accessing a J2EE application using Web services. It also describes the SOAP PHP installation procedure, and relates to the security issues we are discussing here. The article states: "...there's no first-class support in ext/soap for WS-Security. Therefore, if we're going to send and receive WS-Security headers in PHP, we'll have to drop down into a more detailed interface where we can explicitly create the SOAP headers. ...You build up the message elements using the SoapHeader, SoapParam and SoapVarclasses, and then use SoapClient::__call to send the SOAP request and get the response."

    Creating a WS-Security header
    As explained above, WS-Security works by adding security headers to the SOAP messages. The box below lists an example of the required security header for WS-Security basic authentication (for the user myUserName and the password myPass):

    Listing 1. Security header for WS-Security basic authentication
    
      
        
          myUserName
          myPass
        
      

    The challenge here is to utilize generic SOAP extension object constructions in order to create the required headers and integrate them into the SOAP call. The instrument for this task is the SOAP extension's SoapVar data structure, which is defined in the PHP online manual (see the Resources),:
    "SoapVar, is a special low-level class for encoding parameters and returning values in non-WSDL mode. It is just a data holder and does not have any special methods except the constructor. It's useful when you want to set the type property in SOAP request or response. The constructor takes data to pass or return, encoding ID to encode it (SOAP_VAR_ENC in our case) and, optionally, type name, type namespace, node name and node name namespace."
    The procedure for creating the required nested tag is: wrap the Username and Password simple (i.e., without nesting) tags into SoapVar. The result should then be wrapped into another SoapVar, tagged as UsernameToken, which is placed inside the tag. Finally, this tag is placed inside the SOAP header.
    In this case the required wrapping operation is two levels deep. In general the approach described here may also be applied to creating complex SOAP objects of arbitrary nesting levels.
    In the next section, we describe the actual implementation.

    Coding basic security in PHP
    The Security tag of the SOAP header is assembled using the bottom-up approach. First, the UserName and Password tags are expressed as XSD strings. Listing 2 assumes that the $username and $password variables are properly assigned:

    Listing 2. Creating XSD strings from credentials
    $nameSpace = "http://schemas.xmlsoap.org/ws/2003/06/secext";//WS-Security namespace
    $userT = new SoapVar($username, XSD_STRING, NULL, $nameSpace, NULL, $nameSpace);
    $passwT = new SoapVar($password, XSD_STRING, NULL, $nameSpace, NULL, $nameSpace);

    In order to express tag with nested and tags inside, we need to define the intermediate class with private data members: $Username and $Password. Note that while the defined class may have an arbitrary name, the data members must have the same names as the corresponding XML tags. Our implementation creates a class named UsernameT1. The data members are assigned by the constructor.

    Listing 3. The UsernameT1 class definition
    class UsernameT1 {
     private $Username; //Name must be  identical to corresponding XML tag in SOAP header
     private $Password; // Name must be  identical to corresponding XML tag in SOAP header 
     function __construct($username, $password) {
     $this->Username=$username;
     $this->Password=$password;
          }
    }

    Now, we can create the content of the complex XML tag as the SoapVar, whose type is not an XSD_STRING, but SOAP_ENC_OBJECT. In this case (unlike when creating an XSD string) the name of the created XML tag is also passed to the SoapVar constructor.

    Listing 4. Creating a UserNameT1 instance and wrapping it into SoapVar
    $tmp = new UsernameT1($userT, $passwT);
    $uuT = new SoapVar($tmp, SOAP_ENC_OBJECT, NULL, $nameSpace, 'UsernameToken', $nameSpace);

    This SoapVar will be wrapped into a UserNameT2 class with the private data member $UsernameToken. Again, the defined class may have an arbitrary name, but the data member must have the same name as the corresponding XML tag.

    Listing 5. The UsernameT2 class definition
    class UserNameT2 {
      private $UsernameToken;  
      //Name must be  identical to corresponding XML tag in SOAP header
      function __construct ($innerVal){
      $this->UsernameToken = $ innerVal;
      }
    }

    A UserNameT2 instance is created and wrapped into a SoapVar:

    Listing 6. Creating a UserNameT2 instance and wrapping it into SoapVar
    $tmp = new UsernameT2($uuT);
    $userToken = new SoapVar($tmp, SOAP_ENC_OBJECT, NULL, $nameSpace, 
     'UsernameToken', $nameSpace);

    The UsernameToken object is attached to the parent XML tag, using the same method again, and the SoapHeader is now constructed:

    Listing 7. Constricting the security header
    $secHeaderValue=new SoapVar($userToken, SOAP_ENC_OBJECT, NULL, $nameSpace, 
                        'Security', $nameSpace);
    $secHeader = new SoapHeader($nameSpace, 'Security', $secHeaderValue);

    This header will be passed to the method __soapCall() of the SoapClient class as an element in the input_headers array. For example, when the security is the only input header:

    Listing 8. Using the security header
    $client->__soapCall($theMethodName, $theMethodSignature, null, $secHeader );
     
    Creating the method's signature
    Calling __soapCall includes one additional task: the construction of the method's passed arguments, also known as the method's signature. In the case of a simple list of parameters, this is a trivial task which is documented in the PHP Online Manual (see the Resources). This section deals with the more complex case, where the Web service's method includes arrays and object parameters and these parameters may also have members that are themselves arrays or objects.
    As with the security header, we need to create a nested tag by using the SOAP extension's SoapVar data structure. This is needed for each parameter that is an object, and for each object member of any passed array or object.
    The next section introduces a few methods that are capable of packing a complex set of parameters into a valid __soapCall arguments parameter.
    Coding a method signature in PHP
    This section lists code for packing PHP parameters into a SOAP call. In order to do that, a generic definition of input is required. We have defined the input as a PHP array of parameters. The array may include simple types, objects, arrays, nested objects, and nested arrays.
    For example, consider the case of a Web services method storeProblemReport() that gets three parameters: severity (integer), errors (an array of strings), and owner (a record with a member that is also a record). The WSDL part of such a method may contain the following lines:

    Listing 9. Example method and parameters - WSDL definition
    
     
      
      
     
    
     
      
      
     
    
      
       
        
        
        
       
      

    An example of possible values and a value input may be:

    Listing 10. Assigning the example parameters
    $fileInfo->fname = '/usr/src/myDir/getToken.php'
    $fileInfo->line = 7;
    $theOwner->component = 'Parser';
    $theOwner->location = $fileInfo;
    $argumentsList = array ('severity'=> 7,
                            'errors' => array ("empty token","read error", "File open error"),
                            'owner'=> $theOwner));

    Creating the required arguments for soapCall is done by the CreateMethodSignature() function below:

    Listing 11. Creating the required arguments
    function createMethodSignature($theMethod, $paramAr) {
        if (null == $paramAr) 
            return array($theMethod =>null);
        $used = null;
        foreach ($paramAr as $name => $value) {
               if (is_array($value) || is_object($value)) {
                   $used[$name] = createMixedValueSignature($value);
               } else {
                   $used[$name] =  $value;
               }
       }
       return array($theMethod =>$used);
    }
    //---------------------------------------------------------------------
    // inner routine: packing an inner complex parameter into a  SOAP-valid representation 
    function createMixedValueSignature($MixedVals) {
        $mixedParamsList = null;
        if (is_object($MixedVals)) {
            foreach ($MixedVals as $name => $value) {
                  if (is_object($value) || is_array($value)) {
                   $mixedParamsList->$name = createMixedValueSignature($value);
              } else {
                $mixedParamsList->$name = $value;
              }
            }
            // an object needs to be passed as SoapVar
            return new SoapVar($mixedParamsList, SOAP_ENC_OBJECT , NULL, NULL);
        } else { // an array
            foreach ($MixedVals as $name => $value) {
               if (is_object($value) || is_array($value)) {
                 $mixedParamsList[$name] = createMixedValueSignature($value);
               } else {
                 $mixedParamsList[$name] = $value;
               }
            }
            // an array is passed as is !!
            return $mixedParamsList;
        }
    }

    Note that arrays of simple data types or arrays (arrays that do not include objects) need no special treatment. The only reason for processing arrays with the recursive function createMixedValueSignature() is the possibility of inner PHP objects. CreateMethodSignature(), with the above input, is used for producing a valid SOAP argument:

    Listing 12. Calling the example method
    $theMethodSignature = CreateMethodSignature('storeProblemReport', $argumentsList);
    $client->__soapCall('storeProblemReport', $theMethodSignature, null, $secHeader );
     

Calling XML Web Services from Windows Forms

A new aspect of Visual Studio is XML Web services, which provides the ability to exchange messages in a loosely coupled environment using standard protocols such as HTTP, XML, XSD, SOAP, and WSDL. The messages can be structured and typed or loosely defined. Because Web services are based on standard protocols, your Web service applications can communicate with a broad variety of implementations, platforms, and devices. For more information, see XML Web Services in Managed Code.
Web services can be used to enhance the functionality of Windows Forms. Connecting Windows Forms to Web services is as simple as making calls to Web service methods, which are processed on a server that then returns the results of the method call.
There are two types of Web service methods, synchronous and asynchronous. When synchronous Web service methods are called, the caller waits for the Web service to respond before continuing operations. When asynchronous Web service methods are called, you can continue to use the calling thread while waiting for the Web service to respond. This allows you to use the existing set of threads efficiently in the client application. For more information about working with synchronous and asynchronous Web service methods, see Accessing XML Web Services in Managed Code.

Synchronous Web Service Methods

A call to a synchronous Web service method involves calling the method and waiting for the computation to occur on the server and return a value before continuing with the rest of the code in the Windows Form.
To create an XML Web service
  1. Create a Web service application. For more information, see Creating XML Web Services in Managed Code.
  2. In Solution Explorer, right-click the .asmx file and choose View Code.
  3. Create a Web service method that does addition. This following Web service method will take two integers and add them, returning the sum:
    ' Visual Basic
     Public Function WebAdd(ByVal x As Integer, ByVal y As Integer) As Integer
       Return x + y
    End Function
    
    // C#
    [WebMethod]
    public int WebAdd(int x, int y)
    {
       return x + y;
    }
  4. Create another Web service method that does multiplication. The following Web service method will take two integers and multiply them, returning the product:
    ' Visual Basic
     Public Function WebMultiply(ByVal x As Integer, ByVal y As Integer) As Integer
       Return x * y
    End Function
    
    // C#
    [WebMethod]
    public int WebMultiply(int x, int y) 
    {
       return x * y;
    }
  5. From the Build menu, choose Build Solution. You can also browse to the .asmx file you created in this project to learn more about Web services. Your Web service is now available for calling from a Windows Form.
To call an XML Web service synchronously
  1. Create a new Windows application. For more information, see Creating a Windows Application Project.
    Security Note   Calls to Web Methods require a privilege level granted by the System.Net.WebPermisson class. If you are running in a partial-trust context, the process might throw an exception. For more information, see Code Access Security Basics.
  2. Add a reference to the Web service created above. For details, see Adding and Removing Web References.
  3. From the Toolbox, add three TextBox controls and two Button controls. The text boxes will be for the numbers, and the buttons will be used for the calculations and to call the Web service methods.
  4. Set the properties of the controls as follows:
    ControlPropertyText
    TextBox1Text0
    TextBox2Text0
    TextBox3Text0
    Button1TextAdd
    Button2TextMultiply
  5. Right-click the form and choose View Code.
  6. Create an instance of the Web service as a member of the class. You need to know the name of the server where you created the Web service above.
    ' Visual Basic
    ' Replace localhost below with the name of the server where
    ' you created the Web service.
    Dim MathServiceClass As New localhost.Service1()
    
    // C#
    localhost.Service1 MathServiceClass = new localhost.Service1();
  7. Create an event handler for Button1's Click event. For details, see Creating Event Handlers on the Windows Forms Designer.
    ' Visual Basic
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    ' Create instances of the operands and result.
       Dim x, y, z As Integer
    ' Parse the contents of the text boxes into integers.
       x = Integer.Parse(TextBox1.Text)
       y = Integer.Parse(TextBox2.Text)
    ' Call the WebAdd Web service method from the instance of the Web service.
       z = MathServiceClass.WebAdd(x, y)
       TextBox3.Text = z.ToString
    End Sub
    
    // C#
    private void button1_Click(object sender, System.EventArgs e)
    {
    // Create instances of the operands and result.
       int x, y, z;
    // Parse the contents of the text boxes into integers.
       x = int.Parse(textBox1.Text);
       y = int.Parse(textBox2.Text);
    // Call the WebAdd Web service method from the instance of the Web service.
       z = MathServiceClass.WebAdd(x, y);
       textBox3.Text = z.ToString();
    }
    Visual C# Note   Be sure that the necessary code to enable the event handler is present. In this case, it would be similar to the following:
    this.button1.Click += new System.EventHandler(this.button1_Click);
  8. Create an event handler for Button2's Click event in the same fashion, and add the following code.
    ' Visual Basic
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    ' Create instances of the operands and result.
       Dim x, y, z As Integer
    ' Parse the contents of the text boxes into integers.
       x = Integer.Parse(TextBox1.Text)
       y = Integer.Parse(TextBox2.Text)
    ' Call the WebMultiply Web service method from the instance of the Web service.
       z = MathServiceClass.WebMultiply(x, y)
       TextBox3.Text = z.ToString
    End Sub
    
    // C#
    private void button2_Click(object sender, System.EventArgs e)
    {
    // Create instances of the operands and result.
       int x, y, z;
    // Parse the contents of the text boxes into integers.
       x = int.Parse(textBox1.Text);
       y = int.Parse(textBox2.Text);
    // Call the WebAdd Web service method from the instance of the Web service.
       z = MathServiceClass.WebMultiply(x, y);
       textBox3.Text = z.ToString();
    }
    Visual C# Note   Be sure that the necessary code to enable the event handler is present. In this case, it would be similar to the following:
    this.button2.Click += new System.EventHandler(this.button2_Click);
  9. Press F5 to run your application. Enter values into the first two text boxes. When you press the Add button, the third text box should show their sum. When you press the Multiply button, the third text box should show their product.
    Note   The first call to a Web service takes a while for the server to process, because the Web service is instantiated on the server. Keep this in mind when pressing the buttons in your application. This lag is dealt with in the section below.

Asynchronous Web Services

When you call asynchronous Web service methods, the application continues to run while waiting for the Web service to respond. This allows you to use the resources efficiently in the client application. This is a far more resource-savvy way to implement Web services within your Windows application.
For details, see Accessing an XML Web Service Asynchronously in Managed Code.

Calling Web Service using ASP.NET

Shows how to call a Web service inside ASP.NET Web project using a test published Web service: Extentrix Web Services 2.0 Application Edition

Introduction

Web services signal a new age of trivial distributed application development. While Web services are not intended nor do they have the power to solve every distributed application problem, they are an easy way to create and consume services over the Internet. One of the design goals for Web Services is to allow companies and developers to share services with other companies in a simple way over the Internet.
Web services take Web applications to the next level.
Using Web services, your application can publish its function or message to the rest of the world.
Web services use XML to code and decode your data and SOAP to transport it using open protocols.
With Web services, your accounting departments Win 2K servers' billing system can connect with your IT suppliers UNIX server.
Using Web services, you can exchange data between different applications and different platforms.
With Microsoft .NET platform, it is a simple task to create and consume Web Services. In this article, I am going to show how to call a published Web service inside a Web project.
I use a test published Web service; Extentrix Web Services 2.0 Application Edition that Extentrix published for the developer community to help them in testing and developing.
So I'll simply explain the functions of this Web services APIs. In general Extentrix Web Services for Citrix Presentation Server helps you get information about a published application for a specific client with the specified details, server types, and client types. It also returns the ICAfile description to be used to launch an application with a given parameter and checks the user's credentials and returns true if they are valid.
For more information, visit this website.
You can find more samples, use this web service, and test it here.

Background

Knowledge in ASP.NET is preferred.

Using the Code

Simple Steps to Consume a Web Service

  1. Create a Web Site project
  2. Add a Web Reference
  3. Call the Web services APIs inside the code

First Step: Create a Web Site Project

  1. To create a new Web Site project, choose New from File menu, then choose Web Site as shown below:

  2. Choose ASP.NET Web Site. Name the project and click OK:

Second Step: Add a Web Reference

After creating the Web Site project, it�s time to add a Web reference for our Web service.
  1. In the solution explorer, right click the project node, choose Add Web Reference:

  2. A new window with Add Web Reference title will be opened:

    In the URL field, insert the URL for the Web service. In this tutorial, as I mentioned before, I'll use the test published Web services from Extentrix: �Extentrix Web Services 2.0 � Application Edition�.
    After clicking the Go button, you will see the Web services APIs.
  3. Set a name for your Web service reference in the Web reference name field and click Add Reference:

Third Step: Call the Web Services APIs Inside the Code

After successfully adding to the Web service, now we are ready to call the Web services APIs inside our project.
  1. First we need to add the added Web reference to our class.
    ExtentrixWS is the name of the added Web service from the previous step.

    Collapse
    using ExtentrixWS;  
  2. Create a proxy object for our added Web service reference, where ExtentrixWebServicesForCPS is the name of the Web Services.

    Collapse
    //define a Web service proxy object.
    private ExtentrixWS.ExtentrixWebServicesForCPS proxy;
  3. As I explained before, we need credentials to pass to the Citrix Presentation Server. We will pass these credentials through the Web services APIs:

    Collapse
    //define a Citrix Presentation Server Credentials object
    private Credentials credentials;
    Initialize the proxy and the credentials objects:
    Collapse
    //initialize objects
    proxy = new ExtentrixWebServicesForCPS();
    credentials = new Credentials();
  4. Set the values for Citrix credentials. I set the credentials values for the test of Extentrix Web Service:

    Collapse
    //set credentials
    //these values are according to Citrix testdrive presentation server
    //for which Extentrix published a web service for developers to use it
    //as a test web service.
          credentials.Password = "demo";
          credentials.UserName = "citrixdesktop";
          credentials.Domain = "testdrive";
    
    //because it is a sample, we will use no encryption method.
    //so the password will be sent as a clear text.
          credentials.PasswordEncryptionMethod = 0;
    
    //set the domain type to windows domain
          credentials.DomainType = 0;
    Now we can call any Web services available. It is as simple as calling any ordinary function.
  5. Call the GetApplicationsByCredentialsEx Web service. This web service takes the following parameters:
    • Credentials: Citrix Credential to access Citrix Presentation Server Farm
    • Client Name: Pass your machine name
    • Client IP: Pass your machine IP
    • Desired Details : Details you asked for
    • Server Types: Pass �all�
    • Client Types: Pass �all�
I am not going to explain Extentrix Web services APIs, if you are interested, you can go here and look for it.
This API returns an array of ApplicationItemEx. This class will be built for you once you add the Web reference.
This class contains the published application properties. I used this Web service to get all the published applications, and then I created an ImageButton for each application.
Collapse
// 1) Get all the published applications list by calling GetApplicationsByCredentialsEx 
//    web service.
// 2) create an ImageButton for each application
// 3) Create Image for the application
// 4) Add it to the AppList panel.
// 5) Set the event handler for each ImageButton, so when clicking it the associated 
//    application will run calling the web service
ApplicationItemEx[] items = proxy.GetApplicationsByCredentialsEx
    (credentials, Request.UserHostName,
Request.UserHostAddress, new  string[] { "icon","icon-info"}, new string[]{ "all" },
new string[] { "all"});

//loop for each published application
for (int i = 0; i < items.Length; i++) {
//create the ImageButton
System.Web.UI.WebControls.ImageButton app = new System.Web.UI.WebControls.ImageButton();

//set the Image URL to the created image
app.ImageUrl = createIcon(items[i].InternalName,items[i].Icon);

//set the ToolTip to the name of the published application
app.ToolTip = items[i].InternalName;

//add the ImageButton to the AppList panel
AppList.Controls.Add(app);

//set the event handler for the ImageButton.
app.Click += new
System.Web.UI.ImageClickEventHandler(this.OnApplicationClicked);
}
Finally, another example in calling a Web service is to launch the published application.
In this example, in the event handler of the applications ImageButtons I launch the clicked application.
I get the ICA file content by calling LaunchApplication Web service. Then I write the ICA file content to the response to launch the application.
Collapse
private
void OnApplicationClicked (object sender, System.EventArgs e)
{
    ServicePointManager.Expect100Continue = false;

    // Get the event source object.
    System.Web.UI.WebControls.ImageButton app = 
        (System.Web.UI.WebControls.ImageButton)sender;

    //Get the file ICAfile content by calling LaunchApplication web service.
    string = proxy.LaunchApplication(app.ToolTip, credentials, Request.UserHostName, 
            Request.UserHostAddress);

    //Set the response content type to "application/x-ica" to run the file.
    Response.ContentType = "application/x-ica";

    //Run the application by writing the file content to the response.
    Response.BinaryWrite(Response.ContentEncoding.GetBytes(ica));        
    Response.End();
}

Sunday, December 5, 2010

Create a Basic Web Service Using PHP, MySQL, XML, and JSON

Web services are taking over the world. I credit Twitter's epic rise to the availability of a simple but rich API. Why not use the same model for your own sites? Here's how to create a basic web service that provides an XML or JSON response using some PHP and MySQL.

The PHP / MySQL

Copy this code to the clipboard
1/* require the user as the parameter */
2if(isset($_GET['user']) && intval($_GET['user'])) {
3
4 /* soak in the passed variable or set our own */
5 $number_of_posts = isset($_GET['num']) ? intval($_GET['num']) : 10; //10 is the default
6 $format = strtolower($_GET['format']) == 'json' ? 'json' : 'xml'; //xml is the default
7 $user_id = intval($_GET['user']); //no default
8
9 /* connect to the db */
10 $link = mysql_connect('localhost','username','password') or die('Cannot connect to the DB');
11 mysql_select_db('db_name',$link) or die('Cannot select the DB');
12
13 /* grab the posts from the db */
14 $query = "SELECT post_title, guid FROM wp_posts WHERE post_author = $user_id AND post_status = 'publish' ORDER BY ID DESC LIMIT $number_of_posts";
15 $result = mysql_query($query,$link) or die('Errant query: '.$query);
16
17 /* create one master array of the records */
18 $posts = array();
19 if(mysql_num_rows($result)) {
20 while($post = mysql_fetch_assoc($result)) {
21 $posts[] = array('post'=>$post);
22 }
23 }
24
25 /* output in necessary format */
26 if($format == 'json') {
27 header('Content-type: application/json');
28 echo json_encode(array('posts'=>$posts));
29 }
30 else {
31 header('Content-type: text/xml');
32 echo '';
33 foreach($posts as $index => $post) {
34 if(is_array($post)) {
35 foreach($post as $key => $value) {
36 echo '<',$key,'>';
37 if(is_array($value)) {
38 foreach($value as $tag => $val) {
39 echo '<',$tag,'>',htmlentities($val),',$tag,'>';
40 }
41 }
42 echo ',$key,'>';
43 }
44 }
45 }
46 echo '';
47 }
48
49 /* disconnect from the db */
50 @mysql_close($link);
51}
With the number of persons hitting your web service (hopefully), you'll need to do adequate validation before attempting to connect to the database to avoid injection attacks. Once we get the desired results from the database, we cycle through the results to populate our return results array. Depending upon the response type desired, we output the proper header and content in the desired format.
Take the following sample URL for example:
Copy this code to the clipboard
1http://mydomain.com/web-service.php?user=2&num=10
Now, we can take a look at the possible results of the URL.

The XML Output

Copy this code to the clipboard
1<posts>
2 <post>
3 <post_title>SSLmatic SSL Certificate Giveaway Winners</post_title>
4 <guid>http://davidwalsh.name/?p=2304
5 </post>
6 <post>
7 <post_title>MooTools FileManager</post_title>
8 <guid>http://davidwalsh.name/?p=2288
9 </post>
10 <post>
11 <post_title>PHPTVDB: Using PHP to Retrieve TV Show Information</post_title>
12 <guid>http://davidwalsh.name/?p=2266
13 </post>
14 <post>
15 <post_title>David Walsh: The Lost MooTools Plugins</post_title>
16 <guid>http://davidwalsh.name/?p=2258
17 </post>
18 <post>
19 <post_title>Create Short URLs Using U.Nu</post_title>
20 <guid>http://davidwalsh.name/?p=2218
21 </post>
22 <post>
23 <post_title>Create Bit.ly Short URLs Using PHP</post_title>
24 <guid>http://davidwalsh.name/?p=2194
25 </post>
26 <post>
27 <post_title>Represent Your Repositories Using the GitHub Badge!</post_title>
28 <guid>http://davidwalsh.name/?p=2178
29 </post>
30 <post>
31 <post_title>ZebraTable</post_title>
32 <guid>http://davidwalsh.name/?page_id=2172
33 </post>
34 <post>
35 <post_title>MooTools Zebra Table Plugin</post_title>
36 <guid>http://davidwalsh.name/?p=2168
37 </post>
38 <post>
39 <post_title>SSLmatic: Quality, Cheap SSL Certificates and Giveaway!</post_title>
40 <guid>http://davidwalsh.name/?p=2158
41 </post>
42</posts>
Take this next sample URL for example:
Copy this code to the clipboard
1http://mydomain.com/web-service.php?user=2&num=10&format=json
Now, we can take a look at the possible results of the URL.

The JSON Output

Copy this code to the clipboard
1{"posts":[{"post":{"post_title":"SSLmatic SSL Certificate Giveaway Winners","guid":"http:\/\/davidwalsh.name\/?p=2304"}},{"post":{"post_title":"MooTools FileManager","guid":"http:\/\/davidwalsh.name\/?p=2288"}},{"post":{"post_title":"PHPTVDB: Using PHP to Retrieve TV Show Information","guid":"http:\/\/davidwalsh.name\/?p=2266"}},{"post":{"post_title":"David Walsh: The Lost MooTools Plugins","guid":"http:\/\/davidwalsh.name\/?p=2258"}},{"post":{"post_title":"Create Short URLs Using U.Nu","guid":"http:\/\/davidwalsh.name\/?p=2218"}},{"post":{"post_title":"Create Bit.ly Short URLs Using PHP","guid":"http:\/\/davidwalsh.name\/?p=2194"}},{"post":{"post_title":"Represent Your Repositories Using the GitHub Badge!","guid":"http:\/\/davidwalsh.name\/?p=2178"}},{"post":{"post_title":"ZebraTable","guid":"http:\/\/davidwalsh.name\/?page_id=2172"}},{"post":{"post_title":"MooTools Zebra Table Plugin","guid":"http:\/\/davidwalsh.name\/?p=2168"}},{"post":{"post_title":"SSLmatic: Quality, Cheap SSL Certificates and Giveaway!","guid":"http:\/\/davidwalsh.name\/?p=2158"}}]}
Creating a basic web service is very simple and encourages your users to spread the word about your website or service. Want more traffic? Want your website to grow without you putting in all the effort? Create a web service!

Web Services with ASP.NET

Web Services with ASP.NET


  Rob Howard
Microsoft Corporation
February 22, 2001
Web Services are the underpinning of Microsoft's .NET strategy. The concepts and the innovations behind this initiative have struck a chord with developer's building the next generation of Internet applications.
In this month's column, we're going to take a look at the features within ASP.NET to enable Web Services. Before we dig into the technical details let's start with an overview of Web Services.

Web Services Overview

A Web Service is programmable application logic accessible via standard Web protocols. One of these Web protocols is the Simple Object Access Protocol (SOAP). SOAP is a W3C submitted note (as of May 2000) that uses standards based technologies (XML for data description and HTTP for transport) to encode and transmit application data.
Consumers of a Web Service do not need to know anything about the platform, object model, or programming language used to implement the service; they only need to understand how to send and receive SOAP messages (HTTP and XML).

Soap Message

A SOAP message consists of several elements, most notably an envelope. The envelope encapsulates the data transmitted within the SOAP message. Below is a simple SOAP message complete with HTTP headers:

POST /demo/MSDN/PerfCounter.asmx HTTP/1.1
Connection: Keep-Alive
Content-Length: 150
Content-Type: text/xml
Host: localhost
User-Agent: MS Web Services Client Protocol 1.0.2204.19
SOAPAction: "http://tempuri.org/PerfCounters"



               xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
               xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/1999/XMLSchema">
  
    
  
In the example above, we see the HTTP headers for the request, including the HTTP SOAPAction header, which is optionally used by the server for routing the SOAP message. Following the HTTP headers we find the body of the HTTP message. The body of the HTTP message is the SOAP request for a PerfCounters Web Service, which we are going to build.
Unfortunately we don't have nearly enough room in this column to discuss SOAP in depth. To learn more about SOAP, please see the SOAP Developer Resources page. Here you can find the public specification for SOAP 1.1 as well as articles and other relevant resources.

ASP.NET Web Services

Web Services are simple and easy to understand. It is possible, in fact, to author a simple application that surfaces data as XML conforming to the SOAP specification. It would also be relatively straightforward to build an application capable of receiving SOAP messages over HTTP and deriving meaningful value out of it. For those of you familiar with PERL, this could simply be a matter of using RegEx to parse the value out of the XML result; it's just another string.
However, just as we use frameworks such as ASP and ASP.NET to build Web applications, we would much rather use a framework for building Web Services. The reasoning is quite logical. We don't need to reinvent the plumbing—that is, at a high level, the capability to serialize our data as XML, transport the data using HTTP, and de-serialize the XML back to meaningful data. Instead, we want a framework that makes building Web Services easy, allowing us to focus on the application logic not the plumbing. ASP.NET provides this framework for us.
From a developer's point of view, if you have ever written application logic, you have the required skills to author ASP.NET Web Services. More importantly, if you're at all familiar with ASP or ASP.NET application services, (application state memory, and so on) you can also leverage these skills when you build ASP.NET Web Services.

Exposing

For the purpose of example, we're going to write a Web Service that exposes Web application performance counters. Performance counters provide us with details about the behavior of our application, such as the number of active sessions or the number of requests served. We don't always have local server access to our Web server, and if we have a farm of servers we might want to expose the performance counters from all these servers and aggregate them in a central location.

Starting with a Simple Example

Rather than jumping straight into the Performance Counters example, let's start with some very simple application logic so we can see what we need to do to expose our logic as a Web Service. We'll use an Add() method that accepts two Integers and returns their sum. Below is this simple Visual Basic logic:

Public Class MyMath
  Public Function Add(a As Integer, b As Integer) As Integer
    Return a + b
  End Function
End Class
We could use this class and its method as follows:

Dim mymath As new MyMath
Dim result As Integer
result = mymath.Add(10, 20)
To expose the above class, MyMath, as an ASP.NET Web Service we need to move the application logic into a *.asmx file. Just as we use the extension *.aspx for ASP.NET Pages, we use *.asmx to tell ASP.NET that the file is an ASP.NET Web Service.
After we created the *.asmx source file and add our application logic, we need to make a few more small changes:

<%@ WebService Language="VB" Class="MyMath" %>
Public Class MyMath
  Public Function Add(a As Integer, b As Integer) As Integer
    Return a + b
  End Function
End Class

Changes to our source

The changes we've made to the *.asmx file include adding a WebService directive that names both the Language as well as the Class we're exposing as a Web Service. The WebService directive is required, as we must tell ASP.NET the class that contains the application logic. Next, we've added a attribute to our Add() function declaration. An attribute is a declarative code element that lets us change the behavior of our application logic without necessarily writing more code. In the case of the attribute, this tells ASP.NET that the method with this attribute is to be treated as 'Web callable'. Web callable in the sense that ASP.NET does the necessary work for this method to support SOAP.
Now that we've seen what needs to be done to enable application logic as Web callable, let's look at a more relevant sample.

Performance Counter Web Service

Below is application logic that gives us access to the Windows® performance counters, with the changes for ASP.NET Web Services. The file we've created is PerfCounter.asmx:

<%@ WebService language="VB" class="PerfCounters" %>
Imports System.Xml.Serialization
Imports System.Web.Services
Imports System.Diagnostics
 
Public Class PerfCounters
  Inherits WebService
 
  ' Returns a Counter class
  Public Function GetCounters() As Counters
    Dim c As new Counters
 
    ' Application Name
    c.ApplicationName              = IISAppName
 
    ' System specific
    c.WorkerProcessRestarts = Poll(0, "Worker Process Restarts")
    c.WorkerProcessRunning  = Poll(0, "Worker Process Running")
    c.ApplicationsRunning   = Poll(0, "Applications Running")
    c.RequestsQueued        = Poll(0, "Requests Queued")
 
    ' Application Specific
    c.RequestsTotal         = Poll(1, "Requests Total")     
    c.RequestsFailed        = Poll(1, "Requests Failed")   
    c.RequestsSucceeded     = Poll(1, "Requests Succeeded")
    c.ActiveSessions        = Poll(1, "Sessions Active")
 
    Return c
  End Function
 
  Private Function Poll(counterType As Integer, counter As String) As Integer
     Dim PerfCounter As PerformanceCounter
 
     If (counterType = 0)
       PerfCounter = new PerformanceCounter("ASP Plus System", counter, "")
     Else
       PerfCounter = new PerformanceCounter("ASP Plus Applications", counter, IISAppName)
     End If
 
     Return PerfCounter.NextValue().ToInt32()
  End Function
 
  Private Function IISAppName() As String
    Dim AppName As String
    
    AppName = Context.Request.ServerVariables("APPL_MD_PATH")
    AppName = AppName.Replace("/"C, "_"C)
 
    Return AppName
  End Function
End Class
 
Public Class Counters
  Public ApplicationName As String
  Public WorkerProcessRestarts As Integer
  Public WorkerProcessRunning As Integer
  Public ApplicationsRunning As Integer
  Public RequestsQueued As Integer
  Public RequestsTotal As Integer
  Public RequestsFailed As Integer
  Public RequestsSucceeded As Integer
  Public ActiveSessions As Integer
End Class
Again we see that we've declared a WebService directive at the top of the file noting both the language and the class. The class that contains the Web callable method is PerfCounters. Within PerfCounters we find a single method, GetCounters(), with the attribute. GetCounters() returns an instance of another class, Counters.
When we call GetCounters(), the method creates a new instance of the Counter class and begins to set its public members; note, these public members should be implemented as properties, but I chose to save the space for the purpose of the article.
When the Counter class' members are set, we're setting them with the returned result of a call to a private method Poll(). Poll() is responsible for doing the actual work of polling the systems performance counters and returning a result.
Finally, the last method, IISAppName(), returns the value of the server variable APPL_MD_PATH and replaces '/' characters with '_' characters; this value is used as the application name within the performance counters.
Now that we've built the service, let's take a look at how we test it.

Testing Web Services

Now that we've authored this ASP.NET Web Service, how do we test it? The consumer of a Web Service is another application, but ASP.NET provides a simple browser interface to our Web Service that we can use for testing or documentation purposes.
Since our service is exposed as a resource available from our Web server, we can simply open a browser and make a request for that resource. Doing so provides us with a nice HTML-based Web Service Help page that lets people learn about what our service provides:
Figure 1. HTML-based Web Service Help page
ASP.NET generates the above page for us, and we can use it to test our service (note the HTML Invoke button within the GetCounters Web Method section) and access the XML contract language used to describe what our service offers; we'll be coming back to the XML contract language momentarily.
If we press the Invoke button, a new browser window is opened, and a request is made to our service using HTTP-Get; one of the three supported protocols used by ASP.NET Web Services:
Figure 2. Example of the new browser window that is created when pressing the Invoke button.
The XML returned is a valid XML document that describes all of the settings we identified in our Counters class. However, it is not SOAP. SOAP is the default protocol that is used when we do application-to-application communication.
Although we didn't discuss it in this article, we can customize our help page quite extensively. This is done by making some changes to the ASP.NET configuration system, or modifying the DefaultSDLHelpGenerator.aspx. I would recommend not modifying the DefaultSDLHelpGenerator.aspx, as this is the template used for all our Web Services. Instead, make a copy of it and reference the copied version in the application's configuration that makes use of it.
Now that we've discussed authoring and testing our Web Service, let's make use of it.

Consuming

We have several options for consuming Web Services. Since this article is about ASP.NET, we'll focus on .NET technologies that can consume Web Services. However, I should point out that any platform or framework that understands SOAP should be able to communicate with our Web Service. Building the Web Service with ASP.NET does not mean that the service is only available to other Microsoft applications.
Consumers of a Web Service need to know what the service offers—for example, what its Web callable method look like. Therefore, all Web Services optionally share another common XML document: a contract (note, Web Services built with ASP.NET always have a contract provided automatically).

Contract

In the examples above when we discussed testing a Web Service, we didn't discuss the link found within Web Service Help Page: SDL Contract. If we were to follow that link, instead of pressing the Invoke button for the GetCounters() Web Method, we would be presented with the following XML document:
Figure 3. XML document presented when following the link found within the Web Service Help Page
This XML document is a contract that describes our Web Service. It details the protocols supported as well as the semantics for calling and returning values. It additionally defines an XML schema for our Counters class.
Tools can use this XML schema to build proxy classes for our Web Service. A proxy class is a class that looks and feels like a local object, but it is in fact doing the work to serialize, send, receive, and de-serialize our method request to a SOAP endpoint.
Note   Beta 1 of .NET surfaces an "SDL—Service Description Language" contract, Beta 2 will switch to use the more recent "WSDL—Web Service Description Language" contract. Semantically they are very different. WSDL is the collaborative work of Microsoft, IBM, and several other companies to better standardize the XML contract language.
We have various options for consuming Web Services, however, I'd like to call out three in particular:
  • Visual Studio .NET: —Visual Studio .NET does the work of creating the proxy from the SDL or WSDL and adds the appropriate code to our project. This is done by simply selecting Project | Web References, and then pointing at a valid contract. Note that for beta 1 the contract must be SDL.
  • Command Line Tools: —The .NET SDK ships with a tool called WebServiceUtil.exe that accepts an SDL contract and can generate the proxy source code for Visual Basic .NET, C#, or JScript.NET.
  • IE 5.5. Behavior: —A browser specific behavior that allows for rich client interaction with SOAP end-points. For those of you familiar with Remote Scripting, you're going to love this! To learn more about the IE 5.5 behavior, please see WebService Behavior.
Unfortunately, we don't have the space to discuss these three options in detail. However, I thought it would be worthwhile to briefly cover building a proxy with the command line tool, as this is applicable to those who have installed .NET; not just those that have Visual Studio .NET.

Command line tool

.NET, whether you install it as part of Visual Studio .NET or the .NET SDK, includes a command line proxy generation tool called WebServiceUtil.exe. The path to this command line tool, as well as several other command line tools, is added to our path when we installed .NET.
WebServiceUtil.exe allows us to name a SDL, or contract, as one of the command line arguments and the tool can then generate the source code for a proxy to our Web Service.
If, for example, we were to save the SDL from our PerfCounters.asmx example, we could use WebServiceUtil.exe to generate a Visual Basic .NET proxy to this Web Service:

WebServiceUtil.exe /command:proxy PerfCounter.sdl /language:VB
This generates a source file PerfCounters.vb that we now need to compile.
Using the VB.NET command line compiler, vbc.exe, we can compile our VB source file:

vbc /t:library /r:system.web.dll /r:system.web.services.dll /r:system.xml.serialization.dll perfcounters.vb
What we've done with the command line compiler is specify that we want to create a library (dll) rather than an executable (exe), and in addition to naming the source file to compile, we've specified some .NET assemblies (libraries containing classes our source file requires) as arguments to the compiler.
The result is PerfCounters.dll, a complete proxy to our PerfCounters.asmx ASP.NET Web Service that we can now use in .NET applications to communicate via SOAP to our Web Service.
Let's use this proxy to build a simple ASP.NET page that consumes and uses our Web Service.

Using the Web Service

First we need to deploy the compiled proxy, known as an assembly, to a Web application's \bin directory. Although we haven't discussed deploying compiled code in this column yet (yet another topic for a future column), suffice to say that to 'register' an assembly on the system simply requires copying the *.dll to a Web application's \bin directory. This is a feature of .NET, but the use of the \bin directory is specific for ASP.NET.
To make things simple, we'll create a bin directory off of the server's root directory, c:\inetpub\wwwroot\bin for example. A \bin directory must exist in an application root, either the root of the Web or a folder marked as an application in IIS.
Next, we copy our assembly, PerfCounters.dll, to our \bin directory. We can now author our ASP.NET page, which we'll deploy to c:\inetpub\wwwroot. We'll call it PerfCountersConsume.aspx:


Web Application: 

Process Restarts: 

Processes Running: 

Applications Running: 

Requests Queued: 

Requests Total: 

Requests Failed: 

Requests Succeeded: 

Active Sessions: 

The code above creates an instance of our proxy class PerfCounters (available to us since it's a registered assembly in our \bin directory) calls its GetCounters() method and returns an instance of a Counters class. We then use the instance of the Counters class, counters, to request its member variables and populate ASP.NET server controls. The result is below:


Figure 4. ASP.NET server controls

Summary

This column has taken a very high level overview of ASP.NET Web Services. There's quite a bit of detail that we either glossed over or didn't cover at all, for example security, use of session state, extensions, and so on. In next month's column we're going to look at a more advanced feature of ASP.NET Web Services, extensions, that we can use for building attributes that allow us to trace the request/response of our ASP.NET Web Service.
Rob Howard is a program manager for ASP.NET on the .NET Framework team. He spends whatever spare time he has either with his family or fly fishing in Eastern Washington.