Raising Errors

SOAP Encoding Styles

I mentioned in Chapter 5 that the WSDL SOAP extensibility elements define two different encoding styles, Document and RPC. The RPC style of encoding formats the message as described in the section titled “SOAP Encoding” in Chapter 3 and is intended to support procedure-oriented interaction between the client and the server. The Document style of encoding is intended to support document-oriented messages that will be exchanged between the client and the server. See the section titled “Extensibility Elements” in Chapter 4 for more information.

The encoding style affects the format in which the Web service expects SOAP requests to be encoded by the client and how the response received from the Web service will be encoded. ASP.NET Web services have overwhelmingly been showcased as an easy way to facilitate procedure-based communication between the client and the server. But ironically, Document is the default encoding style.

Recall that the default encoding style is set in the WSDL document via the style attribute of the binding extensibility element. You can set the default encoding style value by decorating the class with the SoapDocumentService or SoapRpcService element. Because the InstantQuote Web method is intended to support procedure-oriented communication between the client and the server, the following example uses the SoapRpcService attribute to set the style to RPC:

using System; using System.Web.Services; using System.Web.Services.Protocols; namespace BrokerageFirm {     [WebService(Description="This Web service provides services      related to securities.")]     [SoapRpcService]     public class Securities : WebService     {         [WebMethod(Description="Used to obtain a real-time quote          for a given security.")]         public double InstantQuote(string symbol)         {             double price = 0;             // Implementation...             return price;         }     } }

All methods defined by the Securities class, including InstantQuote, will default to RPC-style encoding. You can override this default by decorating a method with the SoapDocumentMethod attribute. On the other hand, if you want to formally state that the default is Document, you can do so using the SoapDocumentService attribute.

The SoapDocumentService attribute exposes three properties that you can set to control how the SOAP documents are formatted. Of the three, the SoapRpcService attribute supports only the RoutingStyle property. Table 6-4 describes these properties.

Table 6-4  Properties of the SoapDocumentService Attribute

Property

Description

ParameterStyle

Specifies whether the parameters are wrapped in a single element within the body of the SOAP message

RoutingStyle

Specifies whether the HTTP SOAPAction header should be populated or left blank

Use

Specifies whether the default for the encoding style of the messages is Literal or Encoded

Recall from Chapter 4 that the SOAP extension elements also allow you to specify whether the individual messages within an operation are literal or encoded. Literal means that the message must be formatted exactly as dictated by the schema. Encoded means that the message can be encoded as specified. For RPC-style documents, the use attribute is always set to Encoded.

The WSDL SOAP extension elements do not provide a means of specifying a default for the use attribute. Conveniently, the SoapDocumentService attribute does so via the Use property. The ASP.NET runtime will propagate the value of this property to every WSDL message definition. The Use property can be set to one of three values defined in the SoapBindingUse enumeration: Literal, Encoded, or Default. The default is Literal.

The value of the use attribute for RPC-style documents is encoded. The SoapRpcService attribute does not expose a Use property, so the value cannot be changed.

In Chapter 3, I also mentioned that the SOAPAction HTTP header can be empty if the intent of the SOAP message is conveyed in the HTTP request header entry. As you will see in Chapter 9, Universal Description, Discovery, and Integration (UDDI) messages require the HTTPAction header to be empty because each action is posted to a unique URL that conveys the intent of the request.

You can specify whether the SOAPAction header should be populated by setting the RoutingStyle parameter of the SoapDocumentService or SoapRpcService attribute. The RoutingStyle parameter is of type SoapServiceRoutingStyle and can be set to SoapAction or RequestElement. The default is SoapAction.

You can also use the SoapDocumentService attribute to indicate how parameters should be encoded within the body of the message. The ParameterStyle property can be set to one of three values defined in the SoapParameterStyle enumeration: Bare, Wrapped, or Default.

Wrapped means that the parameters will be wrapped within a parent element. The parent element will have the same name as the Web method. Bare means that the parameter elements will appear as direct children of the SOAP body element. The default is Wrapped. Because RPC-style documents follow the encoding style specified by Section 7 of the SOAP specification, parameters are always wrapped.

The SoapDocumentMethod and the SoapRpcMethod attributes are associated with a particular Web method and can be used to override the defaults set by their Web service counterparts. You can also use them to further define how messages sent and received by a Web method should be encoded. Table 6-5 lists the properties exposed by the SoapDocumentMethod attribute. The SoapRpcMethod attribute supports the same set of properties minus the ParameterStyle and Use properties.

Table 6-5  Properties of the SoapDocumentMethod Attribute

Property

Description

Action

Specifies the URI placed in the HTTP SOAPAction header

Binding

Associates a Web method with a particular binding specified by the WebServiceBinding attribute

OneWay

Specifies whether the client will receive a response in association with the request

ParameterStyle

Specifies whether the parameters are wrapped in a single element within the body of the SOAP message

RequestElementName

Specifies the name of the request element within the body of the SOAP message

RequestNamespace

Specifies the namespace URI that contains request element definition

ResponseElementName

Specifies the name of the response element within the body of the SOAP message

ResponseNamespace

Specifies the namespace URI that contains response element definition

Use

Specifies whether the encoding style of the messages is Literal or Encoded

ASP.NET supports two of the four message exchange patterns defined by WSDL, request-response and one-way. So far in this chapter, the examples have all been based on the default message pattern, request-response. Both the SoapDocumentMethod and the SoapRpcMethod attributes expose the OneWay property. When set to true, this property states that no response will be returned to the client when that particular Web method is invoked.

The SoapDocumentMethod and the SoapRpcMethod attributes also allow you to specify the name of the element used to wrap the parameters within the request and response messages. You can set the ResponseElementName and RequestElementName properties to the name assigned to their respective elements.

You can also set the namespace in which the datatype of the request or response element is defined by setting the RequestNamespace or ResponseNamespace property. If this property is not set, the namespace defaults to /encodedTypes relative to the Web service's namespace.

If the ParameterStyle property is set to Wrapped, the properties used to set the element name and the namespace of the response and request messages will be ignored.

Encoding References

When you pass parameters to a remote service, you need to take into account whether the identity of the parameters will be maintained. In some cases, maintaining the identity of the parameters is extremely important. Consider the following Web service, which registers rock climbers for a competition:

public struct Person {     public string Name;     public int Age; } public class ClimbingCompetition {     [WebMethod]     public void Register(Person contestant, Person belay)     {         // Implementation...     } }

The contestant is the individual who will be climbing. The climber will be attached to a rope in case she falls. The belay is the individual who will be holding the other end of the rope on behalf of the climber. With that information in mind, consider the following example:

ClimbingCompetition competition = new ClimbingCompetition(); Person climber = new Person(); competition.Register(climber, climber);

The preceding code contains an error: it registers a climber as both the contestant and the belay. Needless to say, the climber better be darn sure she is not going to fall! Unfortunately, the Register method has no way to capture this runtime error because structures are, by default, passed by value. Therefore, two independent copies of climber will be passed to the Register Web method.

I am fairly certain that the sponsors of the competition would want to ensure that every climber is being belayed. However, unless identity is maintained, the Register method will have no idea whether the contestant and the belay are one and the same. I will explain two potential ways of solving this problem.

The first way is to pass a unique identifier with each of the entities. For example, in addition to passing the person's name and age, you might also want to pass the person's driver's license number (assuming that the driver's license number is guaranteed to be unique). The implementation of the Register method could then check to ensure that the license number of the contestant does not match the license number of the belay.

The second way to solve this problem is to pass the instance of the Person structure by reference. In Chapter 3 you learned how SOAP Encoding specifies the use of the href and id attributes to maintain the identity of a parameter passed by reference. ASP.NET leverages this mechanism to maintain the identities of instances of structures that are passed by reference. Let's take a look at a modified version of the Register method:

public struct Person {     public string Name;     public int Age; } public class ClimbingCompetition {     [WebMethod]     [SoapRpcMethod]     public void Register(ref Person contestant, ref Person belay)     {         // Verify that the contestant and the belay         // are not the same person.         if(Object.ReferenceEquals(contestant, belay))         {             throw new SoapException(                 "The contestant and the belay cannot be the same person.",                 SoapException.ClientFaultCode);         }         // Implementation...     } }

In the preceding example, the Register Web method is decorated with the SoapRpcMethod attribute. This instructs the ASP.NET runtime to serialize the parameters using SOAP Encoding as specified in Section 5 of the SOAP 1.1 specification. In addition, each parameter is decorated with the ref keyword, which indicates that the parameter will be passed by reference. This instructs the ASP.NET runtime to maintain the identity of the instance of the structure passed to the Register method. Note that ASP.NET will maintain identity only for Web methods decorated with the SoapRpcMethod attribute or contained within a class decorated with the SoapRpcService attribute.

Unfortunately, ASP.NET is somewhat inconsistent when handling parameters passed by reference. There is one case in which ASP.NET will not maintain the identity of parameters passed by reference. There is another case in which the identity is maintained, but modifications to the parameters passed by reference are not passed back to the caller. Let's examine each of these situations separately.

With the first issue, the ASP.NET runtime does not properly maintain the identity of parameters when core value types such as Int32 and String are passed by reference. Consider the following example:

public class Issues {     [WebMethod]     [SoapRpcMethod]     public void Issue1(ref int x, ref int y)     {         x += 10;         y += 30;     } }

Since both x and y are decorated with the ref keyword, their values will be round-tripped to the caller. Therefore, any modifications made to the values of x and y by the Issue1 Web method will be reflected on the client. However, because the identities of the parameters are not maintained, your application could be left in an inconsistent state. Consider the following client code:

Issues issues = new Issues(); int z = 10; issues.Issue1(ref z, ref z);

This code leverages a proxy class generated by the ASP.NET WSDL.exe command line utility and generates the following SOAP request message:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"  xmlns:tns="http://tempuri.org/"  xmlns:types="http://tempuri.org/encodedTypes"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema">   <soap:Body     soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">     <tns:Issue1>       <x xsi:type="xsd:int">10</x>       <y xsi:type="xsd:int">10</y>     </tns:Issue1>   </soap:Body> </soap:Envelope>

Notice that two distinct copies of the value of z were encoded into the request message. Unfortunately, the Issue1 Web method has no way of knowing that the parameters x and y actually point to the same variable z on the client and therefore will act on x and y independently. If identity was maintained, z would equal 50 as a result of calling Issue1. However, because identity wasn't maintained, x is set equal to 20 and y is set equal to 40, as shown in the resulting SOAP response message:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"  xmlns:tns="http://tempuri.org/"  xmlns:types="http://tempuri.org/encodedTypes"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema">   <soap:Body     soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">     <tns:Issue1Response>       <x xsi:type="xsd:int">20</x>       <y xsi:type="xsd:int">40</y>     </tns:Issue1Response>   </soap:Body> </soap:Envelope>

The ASP.NET-generated proxy will first set z equal to 20 and then set z equal to 40. Therefore the final state of z will be 40 instead of the correct value of 50. One potential—albeit clumsy—workaround is to wrap the common value type within a structure. The following example demonstrates this technique:

public class Issues {     public struct Parameter     {         public int Value;     }     [WebMethod]     [SoapRpcMethod]     public void Issue1(ref Parameter x, ref Parameter y)     {         x.Value += 10;         y.Value += 30;     } }

Unlike core value types, the ASP.NET runtime will maintain the identity of an instance of a structure that is passed by reference. Unfortunately the ASP.NET runtime will also maintain the identity of an instance of a structure that is passed by value. Therefore, the preceding example will exhibit the same behavior even if the ref keyword is specified. If you do not want the identities of the parameters to be maintained, you can decorate the Web method with the SoapDocumentMethod parameter instead of the SoapRpcMethod parameter.

The final issue is that the ASP.NET runtime will serialize .NET reference types in only the SOAP request message and not the response message. By default, reference types are passed by reference to a method. To achieve this behavior, the client must receive the state of the reference type after the message has returned. However, because the ASP.NET runtime will not serialize parameters containing instances of reference types in the return SOAP response message, the client does not have sufficient information to update its variables accordingly.

If you want parameters containing instances of reference types to be passed by reference, you need to decorate the parameter with the ref keyword. Parameters containing instances of reference types that are decorated with the ref keyword will be serialized in both the SOAP request message and the response message.

Granted, it is helpful to have a means by which to pass data contained in reference types one way across the wire in an effort to reduce the amount of data sent. However, this should be accomplished without overloading the meaning of existing keywords. The meanings of keywords should remain the same whether the code is executed locally or remotely. The companion CD contains sample code for the three issues I describe in this section.



Building XML Web Services for the Microsoft  .NET Platform
Building XML Web Services for the Microsoft .NET Platform
ISBN: 0735614067
EAN: 2147483647
Year: 2002
Pages: 94
Authors: Scott Short

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net