Oct 18 2007

Best Practices in Developing .NET Web services

Category:Bil@l @ 07:58

I have been researching for a while on the standards in developing ASP.NET Web services. I would like to share with you one of the practices that have shown to be a robust and organized one.

To start developing your Web service, you should start with:

1. Decide on the operations that are to be offered by your Web service:

For the sake of the Web service that I will be developing, it is a simple Web service that is called GetStudetRecord(). This method will return information about a student that is identified by a student ID. So in summary we have one Web method:

GetStudentRecord()

 

2. Design the messages (In/Out) for the operations offered:

Having decided on the operations to be offered, it is time to decide on the messages that will be exchanged by the Web service for each operation. In our example, we have to decide on the data type for the input parameter for the GetStudentRecord() method and its return data type.

For the input data type, we will have a parameter of type integer called StudRecID.

For the return data type, we should return an object of type StudRec.

It is better to show the above decisions in a UML simple diagram shown below in Figure 1:

 

You notice that I placed the method signature inside an interface called IStudentCatalog. This interface will be generated by code later on in this article. I will use this interface for both the Web service code-behind file to implement and the business component that will hold the real processing of the GetStudentRecord() method and other methods. This way, both the Web service and business component(s) will implement the same interface and hence the contract is clear between both of them.

 

3. Build the XSD Schema File for the In/Out messages of all operations:

I will be using the XSD Schema designer in VS 2005 to design the XSD file for the StudRec class and the input parameter StudRecID as follows:

xsd

The XSD code is as follows:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema
      id="StudentCatalog"
      targetNamespace="https://bhaidar.net/Schemas/StudentCatalog/"
      elementFormDefault="qualified"
      xmlns="https://bhaidar.net/Schemas/StudentCatalog/"
      xmlns:mstns="https://bhaidar.net/Schemas/StudentCatalog/"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      version="1.0">
  <xs:element name="StudRecID" type="xs:int" />
  <xs:element name="StudRec">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="StudRecID" type="xs:int" />
        <xs:element name="LastName" type="xs:string" />
        <xs:element name="FirstName" type="xs:string" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

 

You can notice the targetNamespace, xmlns, and xmlns:mstns are set to a custom URL, a virtual and not necessarily existing URL for the Schema file.

The XSD complexType is for the StudRec class and the element is for the input parameter StudRecID.

At this step, I created a new Web service project located at: http://localhost/StudentCatalogService/ and placed the above XSD file to the project.

 

4. Design the WSDL Document (Web Service Description Language Document):

Visual Studio can generate the WSDL document for you. But for better control and to make use of the above steps, you can use the following template for the WSDL document to generate your own WSDL document:

<?xml version="1.0" encoding="utf-8"?>
<!--
    1. Change targetNamespace and tns namespace.
       It's easiest if you set this targetNamespace to
       the same value you used for the schema targetNamespace.
-->
<wsdl:definitions
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
    xmlns:s="http://www.w3.org/2001/XMLSchema"
    xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
    xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    targetNamespace="https://bhaidar.net/Schemas/StudentCatalog/"
    xmlns:tns="https://bhaidar.net/Schemas/StudentCatalog/">

  <!--
    2. change the namespace and location to point to your schema
     -->
  <wsdl:import
      namespace="your schema target namespace here"
      location="URL to your schema" />
  <wsdl:types />

  <!--
        3. change message names, be sure to change the corresponding message
           names inside the operations.
        4. Change part names and element names. Element names must
           correspond to elements declared in your schema.
  -->
  <wsdl:message name="OperationNameSoapIn">
    <wsdl:part name="OperationNameSoapInPart1" element="tns:name-of-the-element-in-the-schema" />
  </wsdl:message>
  <wsdl:message name="OperationNameSoapOut">
    <wsdl:part name="OperationNameSoapOutPart1" element="tns:name-of-the-element-in-the-schema" />
  </wsdl:message>

  <!--
         5. Change the port type name.
         6. Change the operation name. This becomes the
            Webmethod's name. Add more operations as needed.
         7. Change the message names here to match the message names above
   -->
  <wsdl:portType name="ServiceNameSoap">
    <wsdl:operation name="OperationName">
      <wsdl:input message="tns:OperationNameSoapIn" />
      <wsdl:output message="tns:OperationNameSoapOut" />
    </wsdl:operation>
  </wsdl:portType>
  <!--
    6. Changes the bdining name to reflect the service name together with the port type name to reflect
        the port type name above.
    7. Change the operations' names
    8. Change the SoapAction to reflect the operation name
     -->
  <wsdl:binding name="ServiceNameSoap" type="tns:ServiceNameSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="OperationName">
      <soap:operation soapAction="https://bhaidar.net/Schemas/StudentCatalog/OperationName" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:binding name="ServiceNameSoap12" type="tns:ServiceNameSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="OperationName">
      <soap12:operation soapAction="https://bhaidar.net/Schemas/StudentCatalog/OperationName" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <!--
    9. Change the service name
    10. Change the port names and binding names to reflect the above names
    11. Change the location of the Web service to reflect its real URL
     -->
  <wsdl:service name="ServiceName">
    <wsdl:port name="ServiceNameSoap" binding="tns:ServiceNameSoap">
      <soap:address location="Location-of-service" />
    </wsdl:port>
    <wsdl:port name="ServiceNameSoap12" binding="tns:ServiceNameSoap12">
      <soap12:address location="Location-of-service" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

 

The above is a WSDL document template generated by Visual Studio 2005. You can follow the comments to know what to change inside the template. Here is a customized version of the above template to fit our needs:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
    xmlns:s="http://www.w3.org/2001/XMLSchema"
    xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
    xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    targetNamespace="https://bhaidar.net/Schemas/StudentCatalog/"
    xmlns:tns="https://bhaidar.net/Schemas/StudentCatalog/">
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="https://bhaidar.net/Schemas/StudentCatalog/">
      <s:element name="StudRecID" type="s:int" />
      <s:element name="StudRec">
        <s:complexType>
          <s:sequence>
            <s:element name="ID" type="s:int" />
            <s:element name="LastName" type="s:string" />
            <s:element name="FirstName" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
    </s:schema>
  </wsdl:types>
  <wsdl:message name="GetStudentRecordSoapIn">
    <wsdl:part name="GetStudentRecordSoapInPart1" element="tns:StudRecID" />
  </wsdl:message>
  <wsdl:message name="GetStudentRecordSoapOut">
    <wsdl:part name="GetStudentRecordSoapOutPart1" element="tns:StudRec" />
  </wsdl:message>
  <wsdl:portType name="StudentCatalogSoap">
    <wsdl:operation name="GetStudentRecord">
      <wsdl:input message="tns:GetStudentRecordSoapIn" />
      <wsdl:output message="tns:GetStudentRecordSoapOut" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="StudentCatalogSoap" type="tns:StudentCatalogSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="GetStudentRecord">
      <soap:operation soapAction="https://bhaidar.net/GetStudentRecord" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:binding name="StudentCatalogSoap12" type="tns:StudentCatalogSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="GetStudentRecord">
      <soap12:operation soapAction="https://bhaidar.net/GetStudentRecord" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="StudentCatalog">
    <wsdl:port name="StudentCatalogSoap" binding="tns:StudentCatalogSoap">
      <soap:address location="http://localhost/StudentCatalogService/StudentCatalog.asmx" />
    </wsdl:port>
    <wsdl:port name="StudentCatalogSoap12" binding="tns:StudentCatalogSoap12">
      <soap12:address location="http://localhost/StudentCatalogService/StudentCatalog.asmx" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

 

It took me less than a minute to customize the WSDL Document for my needs. This is just to tell you how easy it is to configure the template, but off course when you get more operation, it will take some more time. Add the above WSDL document to the Web service project, and hence its path will be something as:

http://localhost/StudentCatalogService/StudentCatalog.wsdl

 

5. Generating the data type objects and Interface contract:

Now that you have the WSDL Document created, it is time to create a new assembly that holds an object representation for the data types created above inside the XML Schema section. In addition, I will create an interface that contains the method signatures to be implemented by the Web service and the business components. To do so, I will use a utility called WSDL.EXE that ships with the .NET framework.

Now, run the Visual Studio Command  Line with the following:

C:\>wsdl.exe /si  /namespace:StudentCatalogTypes /o:StudentCatalog.cs http://localhost/StudentCatalogService/StudentCatalog.wsdl

 

The above line shall generate a new C# object called StudentCatalog that holds an interface (IStudentCatalogSoap) and an object representing the StudRec object as follows:

namespace StudentCatalogTypes {

    using System.Diagnostics;
    using System.Web.Services;
    using System.ComponentModel;
    using System.Web.Services.Protocols;
    using System;
    using System.Xml.Serialization;

    public interface IStudentCatalogSoap {
        [return: System.Xml.Serialization.XmlElementAttribute("StudRec", Namespace="https://bhaidar.net/Schemas/StudentCatalog/")]
        StudRec GetStudentRecord([System.Xml.Serialization.XmlElementAttribute(Namespace="https://bhaidar.net/Schemas/StudentCatalog/")] int StudRecID);
    }

    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="https://bhaidar.net/Schemas/StudentCatalog/")]
    public partial class StudRec {
        private int idField;
        private string lastNameField;
        private string firstNameField;
        /// <remarks/>
        public int ID {
            get {
                return this.idField;
            }
            set {
                this.idField = value;
            }
        }
        /// <remarks/>
        public string LastName {
            get {
                return this.lastNameField;
            }
            set {
                this.lastNameField = value;
            }
        }
        /// <remarks/>
        public string FirstName {
            get {
                return this.firstNameField;
            }
            set {
                this.firstNameField = value;
            }
        }
    }
}

 

Now that a new interface has been created together with a C# object for the StudRec XML Schema.

Add a new Class Library (StudentCatalogTypes) project and place this class inside it. Now build the Class Library and now you got a new assembly (StudentCatalogTypes.dll) that contains the data types for all the messages interchanged and an interface that dictates the methods to be implemented by the Web service.

 

6. Create a new assembly to hold the business components:

The business component to be created in this section shall implement the interface created above and hence provide the real processing and implementation for the methods to be provided by the Web service. For the sake of this article, I will have a single method to be implemented in this assembly:

using System;
using System.Collections.Generic;
using System.Text;

namespace StudentCatalogBusiness
{
    public class StudentCatalogBusiness : StudentCatalogTypes.IStudentCatalogSoap
    {
        #region IStudentCatalogSoap Members

        /// <summary>
        /// Create a new StudentRecord
        /// </summary>
        /// <param name="StudRecID">Student Record ID</param>
        /// <returns>An object of type StudRec</returns>
        public StudentCatalogTypes.StudRec GetStudentRecord(int StudRecID)
        {
            StudentCatalogTypes.StudRec studentRec = new StudentCatalogTypes.StudRec();
            studentRec.ID = 1;
            studentRec.FirstName = "Bilal";
            studentRec.LastName = "Haidar";

            return studentRec;
        }

        #endregion
    }
}

 

After completing this step, we have two assemblies: StudentCatalogTypes and StudentCatalogBusiness. Now as you can see, the functionality has been created through these two assemblies. A new Web application or Windows application can be created to access that functionality. This is very important, you can see that we can live without the Web service. What is left now is to create the Web service.

 

7. Create a new Web service and implement the contract interface:

I will add a new Web service called: StudentCatalog.asmx. In the code-behind file I will implement the IStudentCatalogSoap interface:

using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;

using StudentCatalogTypes;
using StudentCatalogBusiness;

[WebService(Namespace = "https://bhaidar.net/Schemas/StudentCatalog/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class StudentCatalog : System.Web.Services.WebService, IStudentCatalogSoap
{

    #region IStudentCatalogSoap Members

    [WebMethodAttribute()]
    [SoapDocumentMethodAttribute("https://bhaidar.net/Schemas/StudentCatalog/GetStudentRecord", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Bare)]
    [return: System.Xml.Serialization.XmlElementAttribute("StudRec", Namespace = "https://bhaidar.net/Schemas/StudentCatalog/")]
    public StudRec GetStudentRecord(int StudRecID)
    {
        StudentCatalogBusiness.StudentCatalogBusiness studCatBus = new StudentCatalogBusiness.StudentCatalogBusiness();
        StudRec studentRecord = studCatBus.GetStudentRecord(StudRecID);

        return studentRecord;
    }

    #endregion
}

 

As you can see, I implemented the IStudentCatalogSoap interface and provided implementation for the single method found in that interface. Look at the code inside the GetStudentRecord and you will notice that the Web service itself didn't do any work. It delegated the work for the Business component!

Notice the return attribute that has been added to the method which specifies the XML Schema element type to be returned as an XmlElementAttribute and providing the same namesapce for the Web service and the XML Schema.

 

That's it. Now you are ready to consume your Web service from any client application you need.

You can download the complete code of this article from here.

Here is a schematic for the entire process and how the components are interacting with each other:

diagram

 

I truly hope you enjoyed the article and learned some techniques that you can use to improve your work!

Regards

Tags:

Comments are closed