Shaping the WSDL File

In this section, we will take a simple, unadorned WSDL file. Through the use of attributes, we will change the file in every way we possibly can. By doing this, you will see the many ways a SOAP message can be shaped. Each change will be explained so that you understand what was done and what the effects were. ASP.NET will generate the WSDL for us in every instance. For the example, we will look at a simple Web Method that takes a number and returns a structure that supposedly relates to that number. In reality, everything will be hard coded. That's okay. The purpose of this section is to show what happens outside the body of the implementation, not inside it. You will want to follow along with the modifications on your own. The project file, Ch4WSDL, contains the starting point for the discussion and can be obtained from the companion Web site for this book. We will start with the code in Listing 4.1. The name of the file is service1.asmx.vb.

Listing 4.1. The Class We Will Be Modifying in service1.asmx.vb
Imports System.Web.Services

Public Enum CDType
    Mini
    Normal
End Enum

Public Class AudioCD
    Public Name As String
    Public NumTracks As Long
    Public YearPublished As Long
    Public Artist As String
    Public TheType As CDType
End Class

<WebService(Namespace:="http://tempuri.org/")> _
Public Class Service1
    Inherits System.Web.Services.WebService

    <WebMethod()> _
    Public Function GetCD(ByVal CD_ID As Long) As AudioCD
        GetCD = New AudioCD()
        GetCD.Artist = "The Pixies"
        GetCD.NumTracks = 15
        GetCD.YearPublished = 1991
        GetCD.Name = "Trompe le Monde"
    End Function

    <WebMethod()> _
    Public Sub DoSomething(ByVal someArgument As String)
        ' This subroutine does nothing, and that's OK.
    End Sub
End Class
					

The web.config file has been modified so that it will only support SOAP (that is, it has no HTTP POST/GET functionality). To do this, the following lines were added to the web.config file inside the configuration/system.web tag.

<webServices>
    <protocols>
        <remove name="HttpPost" />
        <remove name="HttpGet" />
    </protocols>
</webServices>

Now, when we go to get the WSDL from the Web Service by navigating to http://localhost/ch4wsdl/service1.asmx?WSDL we will have a WSDL that only supports SOAP. This particular version, shown in Listing 4.2, has no extra documentation or decoration and shows what ASP.NET generates without any prompting.

Listing 4.2. The Default WSDL for the Ch4WSDL Web Service, Viewable at http://localhost/Ch4WSDL/Service1.asmx?WSDL
  1:  <?xml version="1.0" encoding="utf-8"?>
  2:  <definitions
  3:      xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
  4:      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  5:      xmlns:s="http://www.w3.org/2001/XMLSchema"
  6:      xmlns:s0="http://tempuri.org/"
  7:      xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
  8:      xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
  9:      xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
 10:      targetNamespace="http://tempuri.org/"
 11:      xmlns="http://schemas.xmlsoap.org/wsdl/">
 12:    <types>
 13:      <s:schema elementFormDefault="qualified"
 14:          targetNamespace="http://tempuri.org/">
 15:        <s:element name="GetCD">
 16:          <s:complexType>
 17:            <s:sequence>
 18:              <s:element minOccurs="1" maxOccurs="1"
 19:                  name="CD_ID" type="s:long" />
 20:            </s:sequence>
 21:          </s:complexType>
 22:        </s:element>
 23:        <s:element name="GetCDResponse">
 24:          <s:complexType>
 25:            <s:sequence>
 26:              <s:element minOccurs="0" maxOccurs="1"
 27:                  name="GetCDResult" type="s0:AudioCD" />
 28:            </s:sequence>
 29:          </s:complexType>
 30:        </s:element>
 31:        <s:complexType name="AudioCD">
 32:          <s:sequence>
 33:            <s:element minOccurs="0" maxOccurs="1"
 34:              name="Name" type="s:string" />
 35:            <s:element minOccurs="1" maxOccurs="1"
 36:              name="NumTracks" type="s:long" />
 37:            <s:element minOccurs="1" maxOccurs="1"
 38:              name="YearPublished" type="s:long" />
 39:            <s:element minOccurs="0" maxOccurs="1"
 40:              name="Artist" type="s:string" />
 41:            <s:element minOccurs="1" maxOccurs="1"
 42:              name="TheType" type="s0:CDType" />
 43:          </s:sequence>
 44:        </s:complexType>
 45:        <s:simpleType name="CDType">
 46:          <s:restriction base="s:string">
 47:            <s:enumeration value="Mini" />
 48:            <s:enumeration value="Normal" />
 49:          </s:restriction>
 50:        </s:simpleType>
 51:        <s:element name="DoSomething">
 52:          <s:complexType>
 53:            <s:sequence>
 54:              <s:element minOccurs="0" maxOccurs="1"
 55:                  name="someArgument" type="s:string" />
 56:            </s:sequence>
 57:          </s:complexType>
 58:        </s:element>
 59:        <s:element name="DoSomethingResponse">
 60:          <s:complexType />
 61:        </s:element>
 62:      </s:schema>
 63:    </types>
 64:    <message name="GetCDSoapIn">
 65:      <part name="parameters" element="s0:GetCD" />
 66:    </message>
 67:    <message name="GetCDSoapOut">
 68:      <part name="parameters" element="s0:GetCDResponse" />
 69:    </message>
 70:    <message name="DoSomethingSoapIn">
 71:      <part name="parameters" element="s0:DoSomething" />
 72:    </message>
 73:    <message name="DoSomethingSoapOut">
 74:      <part name="parameters" element="s0:DoSomethingResponse" />
 75:    </message>
 76:    <portType name="Service1Soap">
 77:      <operation name="GetCD">
 78:        <input message="s0:GetCDSoapIn" />
 79:        <output message="s0:GetCDSoapOut" />
 80:      </operation>
 81:      <operation name="DoSomething">
 82:        <input message="s0:DoSomethingSoapIn" />
 83:        <output message="s0:DoSomethingSoapOut" />
 84:      </operation>
 85:    </portType>
 86:    <binding name="Service1Soap" type="s0:Service1Soap">
 87:      <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
 88:          style="document" />
 89:      <operation name="GetCD">
 90:        <soap:operation soapAction="http://tempuri.org/GetCD"
 91:          style="document" />
 92:        <input>
 93:          <soap:body use="literal" />
 94:        </input>
 95:        <output>
 96:          <soap:body use="literal" />
 97:        </output>
 98:      </operation>
 99:      <operation name="DoSomething">
100:        <soap:operation soapAction="http://tempuri.org/DoSomething"
101:          style="document" />
102:        <input>
103:          <soap:body use="literal" />
104:        </input>
105:        <output>
106:          <soap:body use="literal" />
107:        </output>
108:      </operation>
109:    </binding>
110:    <service name="Service1">
111:      <port name="Service1Soap" binding="s0:Service1Soap">
112:        <soap:address location="http://localhost/ch4wsdl/service1.asmx" />
113:      </port>
114:    </service>
115:  </definitions>
					

I promise, we won't be looking at the WSDL file in its entirety again. We're going to do a top down approach and cover each of the elements in the following order:

  • definitions

  • types

  • messages

  • portType

  • binding

  • service

Each of these sections will also cover how you would modify the parts inside of it. Because the definitions element contains all of the others, I will not cover definitions the same way.

One attribute that does not fit neatly into this discussion is the effect of declaring that a particular Web Service is either RPC/encoded or document/literal. A Web Service that is RPC/encoded exists primarily for easy setup of remote procedure call SOAP messages. The intention of RPC/encoded is to make it possible for a receiver of the SOAP message to reconstruct any data without prior knowledge of the data format. Document/literal SOAP messages are sent with the assumption that the message recipient will know how to interpret the message ahead of time. With WSDL becoming more prevalent for defining message formats, RPC/encoded messaging is becoming less useful as time goes on.

The WSDL file shown in Listing 4.2 shows a document/literal Web Service. By default, all Web Services have the System.Web.Services.Protocols.SoapDocumentService attribute applied to them. You get the same code if you add this attribute yourself. To enable RPC/encoded access to the Web Service, you just apply the System.Web.Services.Protocols.SoapRpcService attribute to the Web Service class. This does change the WSDL quite a bit. Listing 4.3 shows the WSDL for the updated service. The changes from line 1 of the code are visible in the following lines for the WSDL: 37, 40, 43, 58, 61, 63–64, 67–68, 73, 75–76, and 79–80. Also, the [method name]Request/Response elements from the types section are completely missing.

Listing 4.3. Applying the SoapRpcService (Line 1) to the Web Service Class (service2.asmx.vb)
 1:  <System.Web.Services.Protocols.SoapRpcService()> _
 2:  Public Class Service1
 3:  Inherits System.Web.Services.WebService
 4:
 5:      <WebMethod()> _
 6:      Public Function GetCD(ByVal CD_ID As Long) As AudioCD
 7:          GetCD = New AudioCD()
 8:          GetCD.Artist = "The Pixies"
 9:          GetCD.NumTracks = 15
10:          GetCD.YearPublished = 1991
11:          GetCD.Name = "Trompe le Monde"
12:      End Function
13:
14:      <WebMethod()> _
15:      Public Sub DoSomething(ByVal someArgument As String)
16:         ' This subroutine does nothing, and that's OK.
17:      End Sub
18:  End Class

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service2.asmx?wsdl.

 1:  <?xml version="1.0" encoding="utf-8"?>
 2:  <definitions
 3:      xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
 4:      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 5:      xmlns:s="http://www.w3.org/2001/XMLSchema"
 6:      xmlns:s0="http://tempuri.org/"
 7:      xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 8:      xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
 9:      xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
10:      targetNamespace="http://tempuri.org/"
11:      xmlns="http://schemas.xmlsoap.org/wsdl/">
12:    <types>
13:      <s:schema targetNamespace="http://tempuri.org/">
14:        <s:complexType name="AudioCD">
15:          <s:sequence>
16:            <s:element minOccurs="1" maxOccurs="1"
17:              name="Name" type="s:string" />
18:            <s:element minOccurs="1" maxOccurs="1"
19:              name="NumTracks" type="s:long" />
20:            <s:element minOccurs="1" maxOccurs="1"
21:              name="YearPublished" type="s:long" />
22:            <s:element minOccurs="1" maxOccurs="1"
23:              name="Artist" type="s:string" />
24:            <s:element minOccurs="1" maxOccurs="1"
25:              name="TheType" type="s0:CDType" />
26:          </s:sequence>
27:        </s:complexType>
28:        <s:simpleType name="CDType">
29:          <s:restriction base="s:string">
30:            <s:enumeration value="Mini" />
31:            <s:enumeration value="Normal" />
32:          </s:restriction>
33:        </s:simpleType>
34:      </s:schema>
35:    </types>
36:    <message name="GetCDSoapIn">
37:      <part name="CD_ID" type="s:long" />
38:    </message>
39:    <message name="GetCDSoapOut">
40:      <part name="GetCDResult" type="s0:AudioCD" />
41:    </message>
42:    <message name="DoSomethingSoapIn">
43:      <part name="someArgument" type="s:string" />
44:    </message>
45:    <message name="DoSomethingSoapOut" />
46:    <portType name="Service1Soap">
47:      <operation name="GetCD">
48:        <input message="s0:GetCDSoapIn" />
49:        <output message="s0:GetCDSoapOut" />
50:      </operation>
51:      <operation name="DoSomething">
52:        <input message="s0:DoSomethingSoapIn" />
53:        <output message="s0:DoSomethingSoapOut" />
54:      </operation>
55:    </portType>
56:    <binding name="Service1Soap" type="s0:Service1Soap">
57:      <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
58:          style="rpc" />
59:      <operation name="GetCD">
60:        <soap:operation soapAction="http://tempuri.org/GetCD"
61:          style="rpc" />
62:        <input>
63:          <soap:body use="encoded" namespace="http://tempuri.org/"
64:              encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
65:        </input>
66:        <output>
67:          <soap:body use="encoded" namespace="http://tempuri.org/"
68:              encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
69:        </output>
70:      </operation>
71:      <operation name="DoSomething">
72:        <soap:operation soapAction="http://tempuri.org/DoSomething"
73:          style="rpc" />
74:        <input>
75:          <soap:body use="encoded" namespace="http://tempuri.org/"
76:              encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
77:        </input>
78:        <output>
79:          <soap:body use="encoded" namespace="http://tempuri.org/"
80:              encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
81:        </output>
82:      </operation>
83:    </binding>
84:    <service name="Service1">
85:      <port name="Service1Soap" binding="s0:Service1Soap">
86:        <soap:address location="http://localhost/ch4wsdl/service1.asmx" />
87:      </port>
88:    </service>
89:  </definitions>
					

The big difference between the RPC/encoded version and the document/literal version is that the types and resulting messages are different. The document/literal version wraps the types in a request or response-related message. The RPC/encoded version simply passes the specialized data type. This change cascades down into the message, portType, and binding definitions. The RPC/encoded option gives a SOAP endpoint the ability to figure out the object graph without any prior knowledge of any type information. Document/literal typically requires prior knowledge of the document layout. In general, document/literal encoding gives the most flexibility.

definitions

Through the use of .NET attributes you can set the name for two of the XML attributes in the definitions element—xmlns:s0 and targetNamespace. Changes to these namespaces will cascade throughout the WSDL file. The text tempuri.org will be replaced with the new namespace name. How do you do this? The WebService attribute has a member, Namespace, that affects the definitions element. Namespace defines the default root namespace associated with any messages or XSD documents. This will also be used to define any targetNamespace items for the document. As an example, let's set the Namespace member. Listing 4.4 shows the changes to the code as well as the effect on the WSDL file. The change is reflected on the following lines within the WSDL: 1–2, 6, and 13.

Listing 4.4. Applying the WebService Attribute on a Class (service3.asmx.vb)
<WebService( _
    Namespace:="http://www.samspublishing.com/VBWS/Ch4WSDL/")> _
Public Class Service1

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service3.asmx?WSDL.

 1:  <definitions xmlns:s0="http://www.samspublishing.com/VBWS/Ch4WSDL/"
 2:      targetNamespace="http://www.samspublishing.com/VBWS/Ch4WSDL/"
 3:      <!--other attributes deleted--> >
 4:      <types>
 5:          <s:schema elementFormDefault="qualified"
 6:              targetNamespace="http://www.samspublishing.com/VBWS/Ch4WSDL/">
 7:              <!--types deleted-->
 8:          </s:schema>
 9:      </types>
10:      <binding name="Service1Soap" type="s0:Service1Soap">
11:          <operation name="GetCD">
12:              <soap:operation soapAction=
13:                  "http://www.samspublishing.com/VBWS/Ch4WSDL/GetCD"
14:                  style="document" />
15:              <!--input and output deleted-->
16:          </operation>
17:      </binding>
18:      <!--service deleted-->
19:  </definitions>
						

The WebService attribute allows for changes to the service and binding elements as well. We will look at those changes toward the end of this section.

types

Stepping through the WSDL file, the next element up for modification is the types section. You modify the types by setting attributes on the class definition itself. You can set the following information for any given type:

  • targetNamespace for the type. By default, the type will share the targetNamespace stated by the WebService attribute for the Web Service using the type.

  • Declare what the name of the type is when serialized as XML. The default name matches the classname.

  • Specify whether the item is serialized as an element or as an attribute. By default, members will get serialized as elements.

  • Specify the name of the element when serialized as SOAP.

To change the target namespace and the name of the type, you use the System.Xml.Serialization.XmlTypeAttribute class. To set the namespace, you set the value of the Namespace property. The TypeName property will set the name of the type. The XmlTypeAttribute class has a third property, IncludeInSchema. If you set this property to false, the WSDL cannot be generated for any Web Service that uses that type as an argument or return value. Why? Setting that property to false says “do not generate schema for this type.” If .NET cannot create schema, it cannot generate the file that tells others how to use the type.

When the WSDL file needs to include a type that lives outside the default targetNamespace, it creates an extra schema element for that new type inside the types element. Any schemas using that element will import the new namespace into their own namespace. Listing 4.5 shows the code being applied to the AudioCD class and CDType enumeration, as well as the fragment of the WSDL file this change effects. Specifically, the following lines in the WSDL shown in Listing 4.5 have changed: 4–7, 12, 18–20, and 30–31.

Listing 4.5. Setting the targetNamespace and Type Name for a Class (service4.asmx.vb)
 1:  <System.Xml.Serialization.XmlType( _
 2:      Namespace:="http://www.samspublishing.com/VBWS/CDType", _
 3:      TypeName:="CompactDiscType")> _
 4:  Public Enum CDType
 5:      Mini
 6:      Normal
 7:  End Enum
 8:
 9:  <System.Xml.Serialization.XmlType( _
10:      Namespace:="http://www.samspublishing.com/VBWS/CDType", _
11:      TypeName:="MyAudioCD")> _
12:  Public Class AudioCD
13:      Public Name As String
14:      Public NumTracks As Long
15:      Public YearPublished As Long
16:      Public Artist As String
17:      Public TheType As CDType
18:  End Class

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service4.asmx?WSDL.

 1:  <definitions xmlns:s1="http://www.samspublishing.com/VBWS/CDType"
 2:     <!--attributes deleted--> >
 3:    <types>
 4:      <s:schema elementFormDefault="qualified" targetNamespace=
 5:              "http://www.samspublishing.com/VBWS/Ch4WSDL/">
 6:          <s:import namespace=
 7:              "http://www.samspublishing.com/VBWS/CDType" />
 8:          <s:element name="GetCDResponse">
 9:              <s:complexType>
10:                  <s:sequence>
11:                      <s:element minOccurs="0" maxOccurs="1"
12:                          name="GetCDResult" type="s1:MyAudioCD" />
13:                  </s:sequence>
14:              </s:complexType>
15:          </s:element>
16:          <!--types deleted-->
17:      </s:schema>
18:      <s:schema elementFormDefault="qualified"
19:          targetNamespace="http://www.samspublishing.com/VBWS/CDType">
20:          <s:complexType name="MyAudioCD">
21:              <s:sequence>
22:                  <s:element minOccurs="0" maxOccurs="1"
23:                      name="Name" type="s:string" />
24:                  <s:element minOccurs="1" maxOccurs="1"
25:                      name="NumTracks" type="s:long" />
26:                  <s:element minOccurs="1" maxOccurs="1"
27:                      name="YearPublished" type="s:long" />
28:                  <s:element minOccurs="0" maxOccurs="1"
29:                      name="Artist" type="s:string" />
30:                  <s:element minOccurs="1" maxOccurs="1" name="TheType"
31:                      type="s1:CompactDiscType" />
32:              </s:sequence>
33:          </s:complexType>
34:          <s:simpleType name="CompactDiscType">
35:              <s:restriction base="s:string">
36:                  <s:enumeration value="Mini" />
37:                  <s:enumeration value="Normal" />
38:              </s:restriction>
39:          </s:simpleType>
40:      </s:schema>
41:    </types>
42:  </definitions>

You can also change the layout of the individual elements within the type. Using the System.Xml.Serialization.XmlElementAttribute class, you can define how each element gets serialized. For example, you can allow for the items to be nil or not. The name used by XML serialization can also be changed from the default name. How do these changes manifest inside of the WSDL file? If you set the namespace for the given element and that namespace differs from the one used for serializing the class, a separate item will be generated in the WSDL. That element will be contained within a schema element for that namespace and will map the element name to the declared type. Listing 4.6 shows these changes, applied to the AudioCD.Name member variable, in action. The changes are reflected in the WSDL in the listing on lines 9, 12, and 17–20.

The declaration states that the name should be serialized with the name CDName. Our use of the Form property indicates that the Name element must be namespace qualified. When placing an element into another namespace, you cannot set the value to System.Xml.Schema.XmlSchemaForm.Unqualified. An unqualified element will not correctly serialize or deserialize. Furthermore, ASP.NET will generate an exception at runtime when it sees System.Xml.Schema.XmlSchemaForm.Unqualified chosen.

Listing 4.6. Modifying the AudioCD.Name Member Variable (service5.asmx.vb)
1:  <System.Xml.Serialization.XmlElement( _
2:      Form:=System.Xml.Schema.XmlSchemaForm.Qualified, _
3:      ElementName:="CDName", _
4:      IsNullable:=False, _
5:      Namespace:="http://www.scottseely.com/newNS")> _
6:  Public Name As String

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service5.asmx?wsdl.

 1:  <definitions
 2:      xmlns:s2="http://www.scottseely.com/newNS"
 3:      <!--attributes deleted-->>
 4:    <types>
 5:      <!--types deleted-->
 6:      <s:schema elementFormDefault="qualified"
 7:          targetNamespace="http://www.aw.com/VBWS/CDType">
 8:        <s:import
 9:          namespace="http://www.scottseely.com/newNS" />
10:        <s:complexType name="MyAudioCD">
11:          <s:sequence>
12:            <s:element minOccurs="0" maxOccurs="1" ref="s2:CDName" />
13:            <!--elements deleted-->
14:          </s:sequence>
15:        </s:complexType>
16:      </s:schema>
17:      <s:schema elementFormDefault="qualified"
18:          targetNamespace="http://www.scottseely.com/newNS">
19:        <s:element name="CDName" type="s:string" />
20:      </s:schema>
21:    </types>
22:    <!--lots more deleted-->
23:  </definitions>
						

You can even decide to have a member variable get serialized as an attribute. Like an element, you can set the name, namespace, and require that the element is namespace qualified. This is all done through the System.Xml.Serialization.XmlAttributeAttribute class. Listing 4.7 contains a modification to the AudioCD.NumTracks member variable, as well as how these modifications affect the types declaration. The changes to the WSDL due to the changes in the code can be seen on the following lines: 9, 12, and 15–18.

Listing 4.7. Using the XmlAttributeAttribute (service6.asmx.vb)
1:  <System.Xml.Serialization.XmlAttributeAttribute( _
2:      Form:=System.Xml.Schema.XmlSchemaForm.Qualified, _
3:      AttributeName:="NumberOfTracks", _
4:      Namespace:="http://www.scottseely.com/AnotherNewNS")> _
5:  Public NumTracks As Long

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service6.asmx?WSDL.

 1:  <definitions
 2:      xmlns:s3="http://www.scottseely.com/AnotherNewNS"
 3:      <!--attributes deleted-->>
 4:    <types>
 5:      <!--schema deleted-->
 6:      <s:schema elementFormDefault="qualified"
 7:          targetNamespace="http://www.samspublishing.com/VBWS/CDType">
 8:        <s:import namespace="http://www.scottseely.com/newNS" />
 9:        <s:import namespace="http://www.scottseely.com/AnotherNewNS" />
10:        <s:complexType name="MyAudioCD">
11:          <!--elements deleted-->
12:          <s:attribute ref="s3:NumberOfTracks" />
13:        </s:complexType>
14:      </s:schema>
15:      <s:schema elementFormDefault="qualified"
16:          targetNamespace="http://www.scottseely.com/AnotherNewNS">
17:        <s:attribute name="NumberOfTracks" type="s:long" />
18:      </s:schema>
19:    </types>
20:    <!--rest deleted-->
21:  </definitions>
						

Finally, you can also change the names of the enumerated values. For example, the internal names for the CD types may not be what you want the outside to use. To change the name to something else, use the System.Xml.Serialization.XmlEnumAttribute class. This class allows you to change the name of the enumeration by setting its Name property. Listing 4.8 shows how to map the internal type names to a string representing the diameter in millimeters of the CD media to which the type refers.

Listing 4.8. Using the XmlEnumAttribute Class on an Enumeration (service7.asmx.vb)
1:  Public Enum CDType
2:      <System.Xml.Serialization.XmlEnum(Name:="80mm")> _
3:      Mini
4:
5:      <System.Xml.Serialization.XmlEnum(Name:="120mm")> _
6:      Normal
7:  End Enum

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service7.asmx?WSDL.

1:  <s:simpleType name="CDType">
2:      <s:restriction base="s:string">
3:          <s:enumeration value="80mm" />
4:          <s:enumeration value="120mm" />
5:      </s:restriction>
6:  </s:simpleType>
						

The preceding attributes work only when the Web Service is set up to transmit messages using document/literal. While this will certainly be the way most messages are serialized going forward, if you want to specify that the Web Service is RPC-based, you will need to do something else. RPC/encoded Web Services ignore the XML related attributes. Can you accomplish the same task another way? Yes, you can still send an XML document to a caller as a URL-encoded string. URL encoding makes the string “safe” for placing in a URL or as a string by encoding special characters such as <, >, and [.

For comparison to what these attributes do, compare the WSDL elements to the ones shown in Listing 4.4. This next example will show how to set the namespace for the class, as well as how to change the name of an individual element. For an RPC/encoded Web Service, the class namespace and type can be set by using the System.Xml.Serialization.SoapTypeAttribute class. In ASP.NET, you mark the Web Service class with the attribute System.Web.Services.Protocols.SoapRpcServiceAttribute. Just like the XmlTypeAttribute class, SoapTypeAttribute can set the type name for enumeration and class types. To change the element name, use System.Xml.Serialization.SoapElementAttribute. Use System.Xml.Serialization.SoapEnum to change the name of a value within an enumeration. Finally, you can also make it so that certain members will not even show up in the message by applying the System.Xml.Serialization.SoapIgnoreAttribute class to the member variable you do not want to see transmitted, in the listing this is the value NumTracks on lines 21 and 22 of the code. Listing 4.9 shows the code changes and the effects on the WSDL file. The changes are visible on the following lines: 9–10 and 21–22.

Listing 4.9. Applying the SoapTypeAttribute, SoapElementAttribute, SoapIgnoreAttribute, and SoapEnum Classes to the AudioCD Class (service8.asmx.vb)
 1:  <System.Xml.Serialization.SoapType( _
 2:      TypeName:="CompactDiscType", _
 3:      Namespace:="http://www.scottseely.com")> _
 4:  Public Enum CDType
 5:      <System.Xml.Serialization.SoapEnum(Name:="80mm")> _
 6:      Mini
 7:
 8:      <System.Xml.Serialization.SoapEnum(Name:="120mm")> _
 9:      Normal
10:  End Enum
11:
12:  <System.Xml.Serialization.SoapType( _
13:      TypeName:="MyAudioCD", _
14:      Namespace:="http://www.scottseely.com")> _
15:  Public Class AudioCD
16:      <System.Xml.Serialization.SoapElement( _
17:          ElementName:="CDName", _
18:          IsNullable:=True)> _
19:      Public Name As String
20:
21:      <System.Xml.Serialization.SoapIgnore()> _
22:      Public NumTracks As Long
23:      Public YearPublished As Long
24:      Public Artist As String
25:      Public TheType As CDType
26:  End Class
						

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service8.asmx?WSDL.

 1:  <?xml version="1.0" encoding="utf-8" ?>
 2:  <definitions
 3:      xmlns:s0="http://www.scottseely.com"
 4:      <!--attributes deleted-->>
 5:      <types>
 6:          <s:schema targetNamespace="http://www.scottseely.com">
 7:              <s:complexType name="MyAudioCD">
 8:                  <s:sequence>
 9:                      <s:element minOccurs="1" maxOccurs="1"
10:                          name="CDName" type="s:string" />
11:                      <s:element minOccurs="1" maxOccurs="1"
12:                          name="YearPublished" type="s:long" />
13:                      <s:element minOccurs="1" maxOccurs="1"
14:                          name="Artist" type="s:string" />
15:                      <s:element minOccurs="1" maxOccurs="1"
16:                          name="TheType" type="s0:CompactDiscType" />
17:                  </s:sequence>
18:              </s:complexType>
19:              <s:simpleType name="CompactDiscType">
20:                  <s:restriction base="s:string">
21:                      <s:enumeration value="80mm" />
22:                      <s:enumeration value="120mm" />
23:                  </s:restriction>
24:              </s:simpleType>
25:          </s:schema>
26:      </types>
27:      <!--elements deleted-->
28:  </definitions>
						

This wraps up the information on how to shape the layout of elements within the types section of the WSDL file. When you do change the layout of your types, you need to remember that the attribute that applies to the type depends on the type of Web Service. If the Web Service is document/literal, the attributes starting with XML will apply. For RPC/encoded Web Services, use the attributes that start with SOAP.

message, portType, and binding

A lot of the things you do to a message element cascades to the portType and binding elements. As a result, something you do to change the message element will show up in the others. For this section, we will cover the attributes that effect these elements based on the scope of the change. Attributes that affect all three will be discussed first. Attributes that only affect one element or the other will then be discussed. Let's start by looking at the message element and how to mold it.

The message elements are used to define the various messages that flow between the client and the Web Service. By default, each message reflects the name of the function being called. If the function or subroutine is called foo, the request and response messages will be called fooSoapIn and fooSoapOut, respectively. Using the attributes provided by the ASP.NET runtime, you can rename the attributes and the part names and tell ASP.NET to generate messages for only an input/output combo, as well as just an input or just an output message.

To change the message name alone, you can use the System.Web.Services.WebMethodAttribute class. You have to use this attribute to expose the method as a part of the SOAP endpoint. This class has a property, MessageName, that will change the name of both the request and response messages. Listing 4.10 shows how to use this property and its effect on the WSDL file. The WSDL is changed on the following lines in listing 4.10: 7, 15, 18, 27–28, 30–31, 41–42, 50, 52, and 55.

Listing 4.10. Using the WebMethodAttribute.MessageName Property (service9.asmx.vb)
<WebMethod(Messagename:="GetCompactDisc")> _
Public Function GetCD(ByVal CD_ID As Long) As AudioCD

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service9.asmx?WSDL.

 1:  <?xml version="1.0" encoding="utf-8"?>
 2:  <definitions
 3:      <!--attributes deleted-->>
 4:    <types>
 5:      <s:schema elementFormDefault="qualified"
 6:          targetNamespace="http://tempuri.org/">
 7:        <s:element name="GetCompactDisc">
 8:          <s:complexType>
 9:            <s:sequence>
10:              <s:element minOccurs="1" maxOccurs="1" name="CD_ID"
11:                  type="s:long" />
12:            </s:sequence>
13:          </s:complexType>
14:        </s:element>
15:        <s:element name="GetCompactDiscResponse">
16:          <s:complexType>
17:            <s:sequence>
18:              <s:element minOccurs="0" maxOccurs="1" name="GetCompactDiscResult"
19:                 type="s0:AudioCD" />
20:            </s:sequence>
21:          </s:complexType>
22:        </s:element>
23:
24:        <!--Elements deleted-->
25:      </s:schema>
26:    </types>
27:    <message name="GetCompactDiscSoapIn">
28:      <part name="parameters" element="s0:GetCompactDisc" />
29:    </message>
30:    <message name="GetCompactDiscSoapOut">
31:      <part name="parameters" element="s0:GetCompactDiscResponse" />
32:    </message>
33:    <message name="DoSomethingSoapIn">
34:      <part name="parameters" element="s0:DoSomething" />
35:    </message>
36:    <message name="DoSomethingSoapOut">
37:      <part name="parameters" element="s0:DoSomethingResponse" />
38:    </message>
39:    <portType name="Service1Soap">
40:      <operation name="GetCD">
41:        <input name="GetCompactDisc" message="s0:GetCompactDiscSoapIn" />
42:        <output name="GetCompactDisc" message="s0:GetCompactDiscSoapOut" />
43:      </operation>
44:      <!--operation deleted-->
45:    </portType>
46:    <binding name="Service1Soap" type="s0:Service1Soap">
47:      <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
48:          style="document" />
49:      <operation name="GetCD">
50:        <soap:operation soapAction="http://tempuri.org/GetCompactDisc"
51:            style="document" />
52:        <input name="GetCompactDisc">
53:          <soap:body use="literal" />
54:        </input>
55:        <output name="GetCompactDisc">
56:          <soap:body use="literal" />
57:        </output>
58:      </operation>
59:      <!--operation deleted-->
60:    </binding>
61:    <!--service deleted-->
62:  </definitions>
						

The effect on a RPC/encoded Web Service is almost the same. Another change to make to the Web Service is to turn off the output message from the DoSomething Web Method. This method returns nothing, so we might as well not force an empty response element. Like a lot of the attributes we will use in the remainder of this section, one exists for RPC/encoded and for document/literal Web Methods. All of the listings will use the document/literal version of the attribute. These names all begin with SoapDocument. Just substitute SoapRpc for SoapDocument to get the name of the RPC/encoded version of the attribute. To change the element to just an input method, use the OneWay property of the System.Web.Services.Protocols.SoapDocumentMethod attribute. Setting the OneWay property to true tells ASP.NET that the method does not return anything. Listing 4.11 shows it all. Within lines 17–19, notice that the output message is now gone. The same is true within the binding on lines 23–29.

Listing 4.11. Stating that the Web Method Does Not Return Anything (service10.asmx.vb)
1:  <WebMethod(), _
2:   System.Web.Services.Protocols.SoapDocumentMethod( _
3:          OneWay:=True)> _
4:  Public Sub DoSomething(ByVal someArgument As String)

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service10.asmx?wsdl.

 1:  <?xml version="1.0" encoding="utf-8"?>
 2:  <definitions
 3:      <!--attributes deleted-->>
 4:    <types>
 5:      <s:schema elementFormDefault="qualified"
 6:          targetNamespace="http://tempuri.org/">
 7:          <!--DoSomethingResponse does not appear here anymore-->
 8:      </s:schema>
 9:    </types>
10:    <!--messages deleted-->
11:    <message name="DoSomethingSoapIn">
12:      <part name="parameters" element="s0:DoSomething" />
13:    </message>
14:    <!--DoSomethingSoapOut doesn't appear here anymore-->
15:    <portType name="Service1Soap">
16:      <!--operation GetCD deleted-->
17:      <operation name="DoSomething">
18:        <input message="s0:DoSomethingSoapIn" />
19:      </operation>
20:    </portType>
21:    <binding name="Service1Soap" type="s0:Service1Soap">
22:      <!--operation GetCD deleted-->
23:      <operation name="DoSomething">
24:        <soap:operation soapAction="http://tempuri.org/DoSomething"
25:          style="document" />
26:        <input>
27:          <soap:body use="literal" />
28:        </input>
29:      </operation>
30:    </binding>
31:    <!--service element deleted-->
32:  </definitions>
						

You can modify more of the message as well. All this is possible using the SoapDocumentMethodAttribute class. You can change the names of the request and response messages using the RequestElementName, ResponseElementName, RequestNamespace, and ResponseNamespace attributes. You can set the value ASP.NET should expect for the SOAPAction HTTP header using the Action property. Finally, the Binding property lets you name the WSDL binding with which the operation belongs. To use the Binding property, you must define one or more bindings for the class. We will cover using this property a little later in the section. Because all of these items have fairly small effects, they are bundled into one example. Listing 4.12 shows some changes to the GetCD Web Method and how these changes affect the WSDL. The changes are visible on lines 2–3, 7–27, 32–37, 40–43, and 50–51 of the WDSL snippet.

Listing 4.12. Applying (almost) Everything Else Available from the SoapDocumentMethodAttribute (service11.asmx.vb)
 1:  <WebMethod(), _
 2:      SoapDocumentMethodAttribute( _
 3:          RequestElementName:="GetTheCD", _
 4:          RequestNamespace:="http://scottseely.com/GetCDRequest", _
 5:          ResponseElementName:="IGotTheCD", _
 6:          ResponseNamespace:="http://scottseely.com/GetCDResponse")> _
 7:  Public Function GetCD(ByVal CD_ID As Long) As AudioCD
 8:      GetCD = New AudioCD()
 9:      GetCD.Artist = "The Pixies"
10:      GetCD.NumTracks = 15
11:      GetCD.YearPublished = 1991
12:      GetCD.Name = "Trompe le Monde"
13:  End Function

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service11.asmx?WSDL.

 1:  <definitions
 2:      xmlns:s1="http://scottseely.com/GetCDResponse"
 3:      xmlns:s0="http://scottseely.com/GetCDRequest"
 4:      xmlns:s2="http://tempuri.org/"
 5:      <!-- attributes deleted-->>
 6:      <types>
 7:          <s:schema elementFormDefault="qualified"
 8:              targetNamespace="http://scottseely.com/GetCDRequest">
 9:              <s:element name="GetTheCD">
10:                  <s:complexType>
11:                      <s:sequence>
12:                          <s:element minOccurs="1" maxOccurs="1"
13:                              name="CD_ID" type="s:long" />
14:                      </s:sequence>
15:                  </s:complexType>
16:              </s:element>
17:          </s:schema>
18:          <s:schema elementFormDefault="qualified"
19:              targetNamespace="http://scottseely.com/GetCDResponse">
20:              <s:element name="IGotTheCD">
21:                  <s:complexType>
22:                      <s:sequence>
23:                          <s:element minOccurs="0" maxOccurs="1"
24:                              name="GetCDResult" type="s1:AudioCD" />
25:                      </s:sequence>
26:                  </s:complexType>
27:              </s:element>
28:              <!-- types deleted-->
29:          </s:schema>
30:          <!-- schema deleted-->
31:      </types>
32:      <message name="GetCDSoapIn">
33:          <part name="parameters" element="s0:GetTheCD" />
34:      </message>
35:      <message name="GetCDSoapOut">
36:          <part name="parameters" element="s1:IGotTheCD" />
37:      </message>
38:      <!--message deleted-->
39:      <portType name="Service1Soap">
40:          <operation name="GetCD">
41:              <input message="s2:GetCDSoapIn" />
42:              <output message="s2:GetCDSoapOut" />
43:          </operation>
44:          <!--operation deleted-->
45:      </portType>
46:      <binding name="Service1Soap" type="s2:Service1Soap">
47:          <soap:binding
48:              transport="http://schemas.xmlsoap.org/soap/http"
49:              style="document" />
50:          <operation name="GetCD">
51:              <soap:operation soapAction="IAmGettingACD" style="document" />
52:              <!--input/output deleted-->
53:          </operation>
54:          <!--operation deleted-->
55:      </binding>
56:      <!--service deleted-->
57:  </definitions>
						

The following item affects only the message element. The System.Xml.Serialization.SoapElementAttribute class can be used to change the name of the message parts when developing an RPC/encoded Web Service and Web Method. Listing 4.13 shows the code and the changes to the GetCD Web Method.

Listing 4.13. Changing the Name of a Part (service12.asmx.vb)
1:  Public Function GetCD( _
2:     <System.Xml.Serialization.SoapElement( _
3:             ElementName:="CompactDiscID") > _
4:         ByVal CD_ID As Long) As AudioCD

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service12.asmx?WSDL.

1:  <message name="GetCDSoapIn">
2:     <part name="CompactDiscID" type="s:long" />
3:  </message>

That's all you can do that involves the message element. The WebMethodAttribute class offers one other property that is useful to us in terms of shaping the WSDL. The Description property adds a documentation element to the operation element within the portType. You can embed HTML within the operation. This allows you to place links, format text, or do anything else that makes sense for the Web Service documentation. The information you add here will show up in the auto-generated ASP.NET pages for your Web Service. It appears immediately beneath the method name on the main page and on the page that describes the message accepted by the method itself. Listing 4.14 shows the code and the resulting WSDL. The change shows up on lines 3–6 of the WSDL snippet. Figure 4.1 shows the effect to the page for the method.

Figure 4.1. Displaying the documentation that was added via the WebMethodAttribute class.


Listing 4.14. Setting the Description for the Web Method (service13.asmx.vb)
1:  <WebMethod( _
2:      Description:="This is the description. It can contain " & _
3:          "<font color='red'><i>HTML</i></font> " & _
4:          "to enhance the auto-generated pages.")> _
5:  Public Function GetCD(ByVal CD_ID As Long) As AudioCD

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service13.asmx?WSDL.

 1:  <portType name="Service1Soap">
 2:      <operation name="GetCD">
 3:          <documentation>This is the description. It can contain
 4:              &lt;font color='red'&gt;&lt;i&gt;HTML
 5:              &lt;/i&gt;&lt;/font&gt; to enhance the
 6:              auto-generated pages.</documentation>
 7:          <input message="s0:GetCDSoapIn" />
 8:          <output message="s0:GetCDSoapOut" />
 9:      </operation>
10:      <operation name="DoSomething">
11:          <input message="s0:DoSomethingSoapIn" />
12:      </operation>
13:  </portType>
						

You can do some other interesting things to these parts of the WSDL file as well. We will cover separating these pieces into distinct documents in the “Changing the Location of WSDL Elements” section later in this chapter.

service

The only thing you can do that uniquely affects the service element is to add a documentation element. The System.Web.Services.WebServiceAttribute class has a description property. ASP.NET uses the value of this property to fill in the text within the documentation element. Like other documentation elements, this element can contain embedded XML or HTML. Within the WSDL document, any special characters, such as < and > will be encoded as &lt; and &gt;. Listing 4.15 contains the code to add the documentation, as well as the changes to the WSDL, visible on lines 2-4. Figure 4.2 shows the effect on the ASP.NET-generated Web page.

Figure 4.2. Viewing the HTML in the ASP.NET documentation page.


Listing 4.15. Adding Documentation to the service Element (service14.asmx.vb)
1:  <System.Web.Services.WebService( _
2:      Description:="This is some <b><font color='blue'>" & _
3:          "HTML-based</font></b> Web Service Documentation.")> _
4:  Public Class Service1

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service14.asmx?WSDL.

1:  <service name="Service1">
2:      <documentation>This is some &lt;b&gt;&lt;font color='blue'&gt;
3:          HTML-based&lt;/font&gt;&lt;/b&gt;
4:          Web Service Documentation.</documentation>
5:      <port name="Service1Soap" binding="s0:Service1Soap">
6:          <soap:address location=
7:              "http://localhost/ch4wsdl/service1.asmx" />
8:      </port>
9:  </service>

We are not done yet with ways to customize how things get laid out in the WSDL. We have two special topics left to cover—Header and binding information.

Using SOAP Headers

SOAP Headers do influence the layout of the WSDL in many of the same areas that the message itself influences the WSDL. Because SOAP Headers often are specially declared, we cover them separately and look at the various means of shaping them. In many ways, they are just like other classes that get serialized and deserialized. For example, the same attributes that manipulate the form of the header in XML and the WSDL are used on Header member variables. The name of the type when serialized is set the same as any other class. As a result, we will not cover those items again. Instead, we will focus on the things that are unique to a SOAP Header.

A SOAP Header can travel as a part of the request and or response. Depending on the use of the Header, the endpoint may need to be able to understand the Header to correctly process the message. Finally, some part of the class needs to read the Header and make it available to the endpoint. We will cover these items in this section.

To create a Header, create a class that derives from System.Web.Services.Protocols.SoapHeader. The Web Service class that uses the new class needs to contain an instance of that class as a member variable. After you have done that, you need to tie the Header to a particular Web Method. This is accomplished by using the System.Web.Services.Protocols.SoapHeaderAttribute class. This class has several properties that let you state which messages the Header travels (Direction), the name of the member variable associated with the Header (MemberName), and whether or not the Header is required (Required).

For an example, we can add a Header to be used by the DoSomething Web Method. We will make this Header required and state that it only comes in with the request. Listing 4.16 shows the declaration of the MyHeader class. The listing also shows how to use the class within the Web Service to use the Header. You will see that the code writes the information that comes with the SOAP message and writes the data out using the Trace class.

Listing 4.16. Using a SOAP Header (service15.asmx.vb)
 1:  Public Class MyHeader
 2:      Inherits System.Web.Services.Protocols.SoapHeader
 3:
 4:      Public UserName As String
 5:      Public CallTime As DateTime
 6:  End Class
 7:
 8:  <System.Web.Services.WebService( )> _
 9:  Public Class Service1
10:      Inherits System.Web.Services.WebService
11:
12:      Public m_aHeader As New MyHeader()
13:
14:      <WebMethod(), _
15:      System.Web.Services.Protocols.SoapHeader("m_aHeader", _
16:          Required:=True, _
17:          Direction:= _
18:              System.Web.Services.Protocols.SoapHeaderDirection.In)> _
19:      Public Sub DoSomething(ByVal someArgument As String)
20:          Trace.WriteLine("UserName=" & m_aHeader.UserName)
21:          Trace.WriteLine("CallTime=" & m_aHeader.CallTime.ToString())
22:      End Sub
23:
24:  End Class

Yes, the GetCD Web Method was removed from the class so that only the parts pertaining to the SOAP Header were included. This change makes it easier to show the changes made to the Web Service. The biggest surprise to me was how the Header gets mapped to a member variable or property. ASP.NET uses reflection to find and set the value specified in the SoapHeaderAttribute. If the member variable or property is not public, ASP.NET will not generate a WSDL file or any other item related to the Web Service. Why? Code access will not let ASP.NET set the value when translating the SOAP message from XML to objects.

Listing 4.17 shows the changes to the WSDL file. First, a new type gets inserted into the types section in lines 7-15 that describes the Header. Second, the binding for the DoSomething operation includes a reference to the Header on lines 29-32, indicating that it is required for the SOAP call.

Listing 4.17. Changes to the WSDL Document, Viewable at http://localhost/Ch4WSDL/Service15.asmx?WSDL
 1:  <?xml version="1.0" encoding="utf-8" ?>
 2:  <definitions <!-- attributes deleted-->>
 3:      <types>
 4:          <s:schema elementFormDefault="qualified"
 5:              targetNamespace="http://tempuri.org/">
 6:              <!--elements deleted-->
 7:              <s:element name="MyHeader" type="s0:MyHeader" />
 8:              <s:complexType name="MyHeader">
 9:                  <s:sequence>
10:                      <s:element minOccurs="0" maxOccurs="1"
11:                          name="UserName" type="s:string" />
12:                      <s:element minOccurs="1" maxOccurs="1"
13:                          name="CallTime" type="s:dateTime" />
14:                  </s:sequence>
15:              </s:complexType>
16:          </s:schema>
17:      </types>
18:      <!--messages and portType deleted-->
19:      <binding name="Service1Soap" type="s0:Service1Soap">
20:          <soap:binding
21:              transport="http://schemas.xmlsoap.org/soap/http"
22:              style="document" />
23:          <operation name="DoSomething">
24:              <soap:operation
25:                  soapAction="http://tempuri.org/DoSomething"
26:                  style="document" />
27:              <input>
28:                  <soap:body use="literal" />
29:                  <soap:header d5p1:required="true"
30:                      message="s0:DoSomethingMyHeader"
31:                      part="MyHeader" use="literal"
32:                      xmlns:d5p1="http://schemas.xmlsoap.org/wsdl/" />
33:              </input>
34:              <output>
35:                  <soap:body use="literal" />
36:              </output>
37:          </operation>
38:      </binding>
39:      <!--service deleted-->
40:  </definitions>
						

For most of the chapter, I have not written clients for the code because they simply are not interesting. Here, a client is called for no other reason than to show the Trace statements actually working. To get the Web Service set up correctly, you will need to edit the code included on the companion Web site. Remember, you are supposed to be following along with the book open somewhere near a PC for this chapter. (The sample client is included on the companion Web site as listed here.) Listing 4.18 shows that client and the resulting trace from the Web Service. It also shows the SOAP request sent to the service.

Listing 4.18. Client Code for a Console Application that Uses the Header
1:  Sub Main()
2:      Dim svc As New localhost.Service1()
3:      svc.MyHeaderValue = New localhost.MyHeader()
4:      svc.MyHeaderValue.CallTime = DateTime.Now
5:      svc.MyHeaderValue.UserName = "Scott Seely"
6:      svc.MyHeaderValue.MustUnderstand = True
7:      svc.DoSomething("test")
8:  End Sub

Trace output (visible when debugging the Web Service).

UserName=Scott Seely
CallTime=1/1/2002 4:49:39 PM

The SOAP Request

 1:  <?xml version="1.0" encoding="utf-8" ?>
 2:  <soap:Envelope
 3:      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 4:      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5:      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 6:      <soap:Header>
 7:          <MyHeader soap:mustUnderstand="1"
 8:              xmlns="http://tempuri.org/">
 9:              <UserName>Scott Seely</UserName>
10:              <CallTime>2002-01-01T16:49:39.7752016-08:00</CallTime>
11:          </MyHeader>
12:      </soap:Header>
13:      <soap:Body>
14:          <DoSomething xmlns="http://tempuri.org/">
15:              <someArgument>test</someArgument>
16:          </DoSomething>
17:      </soap:Body>
18:  </soap:Envelope>
						

Why did I set MustUnderstand to true? I did this because I know that the Header is required. Had this been a real Web Service, the client would want to make sure that any required elements are understood. Setting MustUnderstand to true is the only way for a client to make sure that this happens.

Changing the Location of WSDL Elements

When declaring a Web Service, you may want the generated WSDL file to contain only information related to the location endpoint. ASP.NET provides three options when distributing the parts of the WSDL file:

  • Place all the information in one document.

  • Place the schema in one document. In a second document place the portType, message, and binding data. A third document will contain the service information.

  • Use the schema, portType, and binding information from a pre-existing WSDL.

By default, all elements will show up in one document. You can get this to happen by doing nothing. To split things up, you need to use a combination of the WebServiceBindingAttribute class and the SoapDocumentMethodAttribute or SoapRpcMethodAttribute class. Using just the WebServiceBindingAttribute class, you can change the name of the binding in the WSDL. This can be done using the Name property of the class. Set this value and the name of the binding changes. Listing 4.19 shows the code to set the value and the effect on the WSDL.

Listing 4.19. Setting the Name of the Binding (service16.asmx.vb)
 1:  <WebServiceBinding(Name:="MyWebServiceBinding"), _
 2:      System.Web.Services.WebService()> _
 3:  Public Class Service1
 4:      Inherits System.Web.Services.WebService
 5:
 6:      <WebMethod(), _
 7:      System.Web.Services.Protocols.SoapDocumentMethod( _
 8:          Binding:="MyWebServiceBinding")> _
 9:      Public Sub DoSomething(ByVal someArgument As String)
10:          Trace.WriteLine("someArgument=" & someArgument)
11:      End Sub
12:
13:  End Class

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service16.asmx?WSDL.

 1:  <portType name="MyWebServiceBinding">
 2:      <!--internals deleted-->
 3:  </portType>
 4:  <binding name="MyWebServiceBinding" type="s0:MyWebServiceBinding">
 5:      <!--internals deleted-->
 6:  </binding>
 7:  <service name="Service1">
 8:      <port name="MyWebServiceBinding" binding="s0:MyWebServiceBinding">
 9:          <!--internals deleted-->
10:      </port>
11:  </service>
						

This is all good, but how do you split things up? UDDI and client creation actually can benefit when the Web Service definition exists independently of any endpoint data. For example, you can register the tModel information and then register a particular implementation as an implementation of that tModel with UDDI. Recall that a tModel is short for “type model” and contains all of the type information particular to a given Web Service. You can then register the fact that an implementation of that tModel exists at some endpoint.

So, how do you make ASP.NET separate everything out? Simply set the Namespace property on the WebServiceBindingAttribute class. By setting this attribute and leaving everything else in the class declaration the same, you will get three documents instead of one. You will get an XSD definition for any elements. The second document will contain everything except the type and endpoint information. The third document defines the service endpoint. Listing 4.20 shows the class declaration and the resulting WSDL.

Listing 4.20. Simple Class Declaration That Causes Segmented WSDL (service17.asmx.vb)
 1:  <WebServiceBinding(Name:="MyWebServiceBinding", _
 2:      Namespace:="http://scottseely.com/MyWS"), _
 3:      System.Web.Services.WebService()> _
 4:  Public Class Service1
 5:      Inherits System.Web.Services.WebService
 6:
 7:      <WebMethod(), _
 8:      System.Web.Services.Protocols.SoapDocumentMethod( _
 9:          Binding:="MyWebServiceBinding")> _
10:      Public Sub DoSomething(ByVal someArgument As String)
11:          Trace.WriteLine("someArgument=" & someArgument)
12:      End Sub
13:
14:  End Class

Schema Definition, viewable at http://localhost/Ch4WSDL/Service17.asmx?schema=schema1.

 1:  <?xml version="1.0" encoding="utf-8" ?>
 2:  <xs:schema xmlns:tns="http://scottseely.com/MyWS"
 3:      elementFormDefault="qualified"
 4:      targetNamespace="http://scottseely.com/MyWS"
 5:      id="schema1" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 6:      <xs:element name="DoSomething">
 7:          <xs:complexType>
 8:              <xs:sequence>
 9:                  <xs:element minOccurs="0"
10:                      maxOccurs="1" name="someArgument"
11:                      type="xs:string" />
12:              </xs:sequence>
13:          </xs:complexType>
14:      </xs:element>
15:      <xs:element name="DoSomethingResponse">
16:          <xs:complexType />
17:      </xs:element>
18:  </xs:schema>
						

Web Service Definition Minus the service Element, viewable at http://localhost/Ch4WSDL/Service17.asmx?wsdl=wsdl1.

 1:  <?xml version="1.0" encoding="utf-8" ?>
 2:  <definitions
 3:      xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
 4:      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 5:      xmlns:s="http://www.w3.org/2001/XMLSchema"
 6:      xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 7:      xmlns:i0="http://scottseely.com/MyWS"
 8:      xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
 9:      xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
10:      targetNamespace="http://scottseely.com/MyWS"
11:      xmlns="http://schemas.xmlsoap.org/wsdl/">
12:      <import namespace="http://scottseely.com/MyWS"
13:          location=
14:              "http://localhost/Ch4WSDL/Service1.asmx?schema=schema1" />
15:      <types />
16:      <message name="DoSomethingSoapIn">
17:          <part name="parameters" element="i0:DoSomething" />
18:      </message>
19:      <message name="DoSomethingSoapOut">
20:          <part name="parameters" element="i0:DoSomethingResponse" />
21:      </message>
22:      <portType name="MyWebServiceBinding">
23:          <operation name="DoSomething">
24:              <input message="i0:DoSomethingSoapIn" />
25:              <output message="i0:DoSomethingSoapOut" />
26:          </operation>
27:      </portType>
28:      <binding name="MyWebServiceBinding" type="i0:MyWebServiceBinding">
29:          <soap:binding
30:              transport="http://schemas.xmlsoap.org/soap/http"
31:              style="document" />
32:          <operation name="DoSomething">
33:              <soap:operation
34:                  soapAction="http://tempuri.org/DoSomething"
35:                  style="document" />
36:              <input>
37:                  <soap:body use="literal" />
38:              </input>
39:              <output>
40:                  <soap:body use="literal" />
41:              </output>
42:          </operation>
43:      </binding>
44:  </definitions>
						

Web Service Definition Including the service Element, viewable at http://localhost/Ch4WSDL/Service17.asmx?WSDL.

 1:  <?xml version="1.0" encoding="utf-8" ?>
 2:  <definitions
 3:      xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
 4:      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 5:      xmlns:i1="http://scottseely.com/MyWS"
 6:      xmlns:s="http://www.w3.org/2001/XMLSchema"
 7:      xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 8:      xmlns:tns="http://tempuri.org/"
 9:      xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
10:      xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
11:      targetNamespace="http://tempuri.org/"
12:      xmlns="http://schemas.xmlsoap.org/wsdl/">
13:      <import namespace="http://scottseely.com/MyWS"
14:          location=
15:            "http://localhost/Ch4WSDL/Service1.asmx?schema=schema1" />
16:      <import namespace="http://scottseely.com/MyWS"
17:          location=
18:            "http://localhost/Ch4WSDL/Service1.asmx?wsdl=wsdl1" />
19:      <types />
20:      <service name="Service1">
21:          <port name="MyWebServiceBinding"
22:              binding="i1:MyWebServiceBinding">
23:              <soap:address
24:                  location="http://localhost/Ch4WSDL/Service1.asmx" />
25:          </port>
26:      </service>
27:  </definitions>
						

The last WSDL document uses WSDL import statements to bring in the schema and other information into the last WSDL document. If you look at the location attribute in the second and third WSDL definitions, you will see that ASP.NET is automatically generating the extra documents as well through the query string. This query string only returns information when the separate namespace exists. Otherwise, ASP.NET will generate nothing for you.

If you are actually implementing a Web Service whose primary definition resides elsewhere, you can tell ASP.NET not to generate the first two documents shown in Listing 4.21. Of course, the Web Service definitions must exist somewhere. To tell ASP.NET where that is, the WebServiceBindingAttribute has one more property you can set—Location. This will cause ASP.NET to include an import element that points to the WSDL document at the specified URL. Listing 4.21 shows the code needed to make this change and the resulting WDSL document with the new location reflected on line 14 of the WSDL portion. If you are following along with the text, do not try to actually create a proxy with the resulting WSDL. It will fail because the location property points to a document that does not actually exist.

Listing 4.21. Source Code to Set the Location of the WSDL (service18.asmx.vb)
1:  <WebServiceBinding(Name:="MyWebServiceBinding", _
2:      Namespace:="http://scottseely.com/MyWS", _
3:      Location:="http://scottseely.com/MyWS/Ch4BindingEx.WSDL"), _
4:      System.Web.Services.WebService()> _
5:  Public Class Service1

Effect on WSDL, viewable at http://localhost/Ch4WSDL/Service18.asmx?WSDL.

 1:  <?xml version="1.0" encoding="utf-8" ?>
 2:  <definitions
 3:      xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
 4:      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 5:      xmlns:s="http://www.w3.org/2001/XMLSchema"
 6:      xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 7:      xmlns:i0="http://scottseely.com/MyWS"
 8:      xmlns:tns="http://tempuri.org/"
 9:      xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
10:      xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
11:      targetNamespace="http://tempuri.org/"
12:      xmlns="http://schemas.xmlsoap.org/wsdl/">
13:      <import namespace="http://scottseely.com/MyWS"
14:          location="http://scottseely.com/MyWS/Ch4BindingEx.WSDL" />
15:      <types />
16:      <service name="Service1">
17:          <port name="MyWebServiceBinding"
18:              binding="i0:MyWebServiceBinding">
19:              <soap:address
20:                  location="http://localhost/Ch4WSDL/Service1.asmx" />
21:          </port>
22:      </service>
23:  </definitions>
						

That's about it for shaping your WSDL. Using the information in this section, you should be able to do anything you need to do with respect to your WSDL document.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.26.138