The Structure of WSE


Before we look at using WSE in code, you must have an understanding of how WSE integrates with the existing .NET Web service infrastructure. At its core, WSE is fundamentally a mechanism for applying advanced Web service protocols to a SOAP envelope. SOAP headers are written to an outgoing message or read from an incoming message, and the body of the message is modified as required.

WSE is implemented as a pipeline architecture that modifies the SOAP envelope of a message as it’s sent or received, and the same architecture is used on both the client and the server. Although clients and servers use the same pipeline architecture, the point at which the message is passed through the pipeline is different. What the pipeline accomplishes is the same. We’ll look at how WSE is integrated with the existing .NET implementation of Web services shortly.

The WSE Pipeline

As we’ve pointed out, WSE uses a pipeline to modify the SOAP envelopes that are transmitted between Web services. This pipeline is unique to WSE and is not to be confused with any of the other pipelines used by .NET.

In the pipeline is a series of filters that modify the SOAP envelopes—input filters for incoming messages and output filters for outgoing messages. All messages leaving a process are passed through the output filters, and all messages arriving at a process are passed through the input filters.

Note

The WSE documentation doesn’t have a specific name for the collections of filters in the pipeline; it simply refers to them as input filters and output filters. We’ll use input pipeline and output pipeline to refer to the groups of filters to avoid any confusion about whether we’re talking about the groups or the individual filters in the groups.

As we mentioned, the current release of WSE implements five of the current GXA specifications. Four of them—WS-Referral, WS-Routing, WS-Security, and WS-Timestamp—are implemented as filters in the pipeline. The fifth, WS-Attachments, is implemented outside of the pipeline, as you’ll soon see.

WSE also defines an additional filter that doesn’t implement a GXA specification and that can be thought of as a “helper” filter. The Trace output filter allows you to easily capture the SOAP envelope that is being transmitted, and the Trace input filter allows the received SOAP envelope to be captured—a feature that’s missing from the standard .NET Web services architecture.

The five sets of filters provided with WSE are listed in Table 13-1.

Table 13-1: Default Filters in WSE

Namespace

Input Filter

Output Filter

Diagnostics

TraceInputFilter

TraceOutputFilter

Referral

ReferralInputFilter

ReferralOutputFilter

Routing

RoutingInputFilter

RoutingOutputFilter

Security

SecurityInputFilter

SecurityOutputFilter

Timestamp

TimestampInputFilter

TimestampOutputFilter

By default, all of the filters in both the input and output WSE pipelines are enabled, although the Trace filters require some additional configuration before they will capture the SOAP envelopes.

The one specification that WSE supports that’s not implemented as a filter is WS- Attachments. As you’ll recall from Chapter 12, adding an attachment to a message requires the entire message to be constructed as a Direct Internet Message Encapsulation (DIME) message with the SOAP envelope for the message being in the first DIME record. On sending a message, this can occur only after you’ve done all the processing on the message; on receiving a message, this must occur before you do any processing on the message. If the message to be sent doesn’t have any attachments, you don’t construct a DIME message; if the message you receive isn’t a DIME message, you don’t try to split it.

In Figure 13-1, you can see the filters that messages are passed through, as well as the point at which the DIME message, if required, is created or split.

click to expand
Figure 13-1: Message path through the WSE pipeline and the filters

As you can see in the figure, you process the filters in one direction for an outgoing message and in the other direction for an incoming message. You do this because you must remove details specific to a filter in the opposite order that they were applied to the message.

Consider the case in which you encrypt a message. When you send a message, you pass it through the output Security filter after it has already been through the Referral, Routing, and Timestamp output filters. As you’ll see in Chapter 15, the Security filter encrypts everything that the message contains, including the details added by the other filters. (Well, this is not strictly true, as you’ll see in Chapter 15, but for our purposes here it’s close enough.) When you receive the message at the other end, you must remove the encryption before you can do anything with the message—the filters in the input pipeline can’t process the message if all they have access to is the encrypted version of the message.

You can see that even though you have matching input and output filters, they perform significantly different tasks. Output filters take the settings you specify and use them to modify the message you’re sending. Input filters extract the relevant information from the message, modify the message if required, and tell you their settings. If an input filter detects anything wrong with the format of the message (such as the Security input filter detecting an invalid security signature), it will raise an exception that’s passed back to the client.

We mentioned that output filters allow “the settings” to control their operation and that input filters tell us “their settings,” but we haven’t explained the mechanism for doing this: the SoapContext object.

The SoapContext Object

WSE defines a new class, SoapContext, that allows you to interact with the filters in the pipeline. WSE uses a request SoapContext that is queried by output filters and a response SoapContext that is populated by input filters.

When you create a proxy for a Web service, the request SoapContext object is valid for the lifetime of the proxy—you can set values on the request SoapContext and use those settings for all calls to the Web service. Only one response SoapContext object is available, but this is overwritten with every call you make to the Web service.

Note

The request SoapContext object is valid for the lifetime of the proxy, but a problem occurs when you attempt to reuse attachments across multiple calls to the Web service. If you try to reuse an attachment, an ObjectDisposedException is thrown. You should avoid using the same proxy object to make multiple calls to a Web service that uses attachments because your code will generate an exception.

At the server, when a request is received, request and response SoapContext objects are created that exist only for the processing of that request. Each request to a Web service has its own SoapContext objects.

You’ll see shortly when we look at the client and server specifics how you actually access the SoapContext objects. For now, the six most important properties of the SoapContext object are listed in Table 13-2.

Table 13-2: Important Properties of the SoapContext Object

Property

Description

Attachments

Lets you get or set the DIME attachments for the message.

Envelope

For incoming messages, retrieves the SOAP envelope, as a SoapEnvelope object, that the current SoapContext was populated from. For outgoing messages, this is undefined.

Path

Lets you get or set the routing information for the message.

Referrals

Lets you get or set the referral information for the message.

Security

Lets you get or set the security information for the message.

Timestamp

Lets you get or set the timestamp information for the message.

We’ll look more closely at five of these properties later in this chapter and in the next two chapters. The sixth property, Envelope, returns the received SOAP envelope after it has been through the input pipeline. As we pointed out, input filters modify the message by removing the SOAP headers specific to WSE and change the body of the message according to the details contained in the relevant headers. The SoapEnvelope object that we retrieve contains the incoming message after it has been passed through the input pipeline and has been stripped of all the WSE-specific details.

WSE does a superb job of shielding you from having to interact with SoapEnvelope objects, and it populates the SoapContext object with the information that has been extracted. However, we’ll have to deal with instances of SoapEnvelope when we create custom filters later in this chapter and when we create custom routers in Chapter 14.

The SoapEnvelope Object

The SoapEnvelope object is a representation of the message that is received. The SOAP envelope received is an XML document, so it makes sense that the SoapEnvelope object exposes the underlying envelope. It does this by inheriting from the System.Xml.XmlDocument class. You can interrogate this object using the standard methodology.

In addition to the properties and methods inherited from System.Xml.XmlDocument, the SoapEnvelope class exposes three properties that are shortcuts to the different parts of the XML document and make the task of using the SoapEnvelope class a little easier.

The Body, Envelope, and Header properties return System.Xml.XmlElement objects that contain the different parts of the SOAP envelope minus the element they’re contained in. The Body property returns the SOAP body of the message without the <soap:Body> container element, Envelope returns the elements contained by <soap:Envelope>, and Header returns the elements contained by <soap:Header>.

WSE at the Client

When you’re creating a proxy for a Web service, the tools provided by Visual Studio .NET and .NET generate a class that inherits from the SoapHttpClientProtocol base class. The proxy class exposes methods that represent the methods on the Web service, and these ultimately call the Invoke method on the SoapHttpClientProtocol base class.

WSE is integrated at the client side by a new class, WebServicesClientProtocol, that inherits from the SoapHttpClientProtocol class. If you use this as the base class for the Web service proxies, the messages you send and receive will automatically pass through the input and output pipelines. You’ll see shortly how this actually happens; for now, just have faith that it does work.

The tools provided with Visual Studio .NET and the .NET Framework always build proxies that inherit from SoapHttpClientProtocol. You must manually change the autogenerated code to inherit from the WebServicesClientProtocol class instead.

Note

In addition to changing the class that the proxy inherits from when you first create the proxy, you must also change the class every time you update the reference to the Web service. This can quickly become very tedious—you’ll soon see a tool that helps you overcome this problem.

Those are the only changes you must make to use WSE in a client application. You have access to the SoapContext objects by using two properties that the proxy inherits from WebServicesClientProtocol. The RequestSoapContext property of the proxy allows you to access the request SoapContext, and you have access to the response SoapContext by using the ResponseSoapContext property. You simply set the values on the RequestSoapContext and then call the method required on the proxy. When the method returns, you can check the ResponseSoapContext for the WSE details returned by the Web service.

To see how easy it is to use WSE on the client, have a look at the following code, taken from an upcoming example. The only change you need to make to the proxy code is shown in italic:

[System.Web.Services.WebServiceBindingAttribute(Name="DiagnosticsWSSoap",  Namespace="http://www.notashop.com/wscr")] public class DiagnosticsWSWse:  Microsoft.Web.Services.WebServicesClientProtocol { 

If you now look at the Object Browser in Visual Studio .NET, as shown in Figure 13-2, you can see that the proxy class does inherit from the correct class and that you have access to the request and response SoapContext objects via the RequestSoapContext and ResponseSoapContext properties.

click to expand
Figure 13-2: The proxy class after manual editing to support WSE

Under the Covers

When you use a proxy to access a Web service, you ultimately call the Invoke method of the proxy class. The proxy doesn’t implement an Invoke method—what you actually call is the Invoke method of the base class. Before WSE this was the Invoke method of the SoapHttpClientProtocol class. With WSE, you use a different base class, WebServicesClientProtocol, for the proxies. As you can see in Figure 13-2, this class doesn’t have an Invoke method. So how does this work? In the same way that a proxy calls the Invoke method on its base class—Inheritance!

WebServicesClientProtocol inherits from SoapHttpClientProtocol, and that class does have the Invoke method. When a call is made to Invoke, the call is handled by the first Invoke method that is found in the inheritance chain. In this case, it’s the Invoke method of the SoapHttpClientProtocol class. However, to fully understand what’s going on you need to look under the covers at what Invoke actually does.

Figure 13-3 shows a simplified view of the internal process of the Invoke method.

As you can see, the Invoke method calls the GetWebRequest method, which returns an HttpWebRequest object. You serialize the outgoing request into the HttpWebRequest object and then pass it into the GetWebResponse method, which uses the HttpWebRequest object to make the call to the Web service. On completion of the call to the Web service, Invoke is returned an HttpWebResponse object, from which we deserialized the returned message. There’s nothing new here, and we already covered this in detail earlier in the book. What’s important is how WSE builds on the existing structure and uses inheritance to facilitate the use of the input and output pipelines in the call to the Web service.


Figure 13-3: Internal architecture of SoapHttpClientProtocol.Invoke

Closer inspection of Figure 13-2 reveals that the WebServicesClientProtocol class implements its own GetWebRequest and GetWebResponse methods, and when Invoke makes a call to either of these methods, the versions implemented by WebServicesClientProtocol are called. This is the key to the integration of WSE with the existing Web service calling mechanism.

The Invoke method expects GetWebRequest and GetWebResponse to return WebRequest and WebResponse objects, respectively. You’ve seen that when you use the methods exposed by SoapHttpClientProtocol, it returns HttpWebRequest and HttpWebResponse objects instead, which are derived from the two classes that Invoke expects. Not surprisingly, WSE has its own derived classes that are used in place of SoapWebRequest and SoapWebResponse.

The SoapWebRequest and SoapWebResponse classes inherit from WebRequest and WebResponse, respectively, and implement the functionality required by WSE. The GetWebRequest method implemented by WebServicesClientProtocol returns a SoapWebRequest object, and GetWebResponse expects a SoapWebRequest object passed to it and returns a SoapWebResponse object. The Invoke method treats these in the same way that it does any other derived classes, and the call to the Web service is made and the WSE pipeline is used.

You also saw in Chapter 5 that it’s possible to call Web services without creating a proxy class. We did this by creating an instance of the HttpWebRequest class and calling the GetResponse method. This made a call to the Web service and returned an HttpWebResponse object.

To manually call a Web service and still use WSE, you simply create a SoapWebRequest instead of an HttpWebRequest. When returning from the GetResponse method, you’re returned a SoapWebResponse object instead of an HttpWebResponse object.

And how do you access the SoapContext for the two new classes? Simple. The SoapWebRequest and SoapWebResponse classes each expose a SoapContext property that returns the SoapContext that’s being used for the current operation.

WSE at the Server

Now that we’ve looked at how WSE is implemented on the client side, it’s time to look at how it’s implemented on the server. WSE on the server is implemented as a SOAP extension. We looked closely at SOAP extensions in Chapter 10.

You integrate the WSE Soap Extension into the .NET Web service infrastructure by simply adding the WSE Soap Extension class to the <soapExtensionTypes> element of the configuration file:

<configuration>   <system.web>     <webServices>       <soapExtensionTypes>         <!-- the TYPE attribute must be on one line  -->         <add type="Microsoft.Web.Services.WebServicesExtension,Π          Microsoft.Web.Services,Version=1.0.0.0, Culture=neutral,Π          PublicKeyToken=31bf3856ad364e35" priority="1" group="0" />       </soapExtensionTypes>     </webServices>   </system.web> </configuration>

As with most configuration details in .NET, you can add the SOAP extension details to any of the configuration files you want, although the standard practice is to apply the configuration only at the level you need it. In most cases, this is the web.config file for the application that’s hosting the Web services.

Note the use of the priority and group attributes in our SOAP extension addition to the configuration files. For WSE to function correctly, you must have it executed first (or last for outgoing messages) in the chain of SOAP extensions. Setting a priority of 1 and a group value of 0 indicates that you want to run the WSE SOAP extension with the highest possible priority. You can change these values and give the WSE SOAP extension any priority you want, but this can lead to unpredictable results.

That’s it. You don’t need to make any more changes to the server code, and the incoming and outgoing SOAP messages are passed through the input and output pipelines as part of the normal SOAP extensions process.

The one thing we’re missing is access to the SoapContext objects used by the request and the response. Unlike on the client side, where you have direct access to them on the proxy class, you access them on the server by calling static methods of the HttpSoapContext class within the called method itself. We have static RequestContext and ResponseContext properties that return the SoapContext for the current call.

Under the Covers

When you use the WSE SOAP extension, WebServicesExtension, the process for calling the methods on the extension is the same as for all other SOAP extensions. The runtime makes repeated calls to the ProcessMessage method of the extension, passing in the SoapMessage object that’s being handled; WebServicesExtension deals with two of these calls to pass the message through the pipeline.

ProcessMessage is first called with SoapMessageStage set to BeforeDeserialize. The WebServicesExtension instance takes the SoapMessage object and performs the necessary steps on it. If SoapMessage is a DIME message, it is split into its component parts and the SOAP envelope is passed through the input pipeline, populating the request SoapContext as it goes.

The handler continues with its normal processing cycle—it deserializes the message and calls the intended method—which has access to both the request SoapContext and the response SoapContext.

When the called method completes and control is passed back to the .NET runtime, ProcessMessage is called with SoapMessageStage set to AfterSerialize and performs the processing required for outgoing messages. The SoapMessage object is passed through the output pipeline using the details specified in the response SoapContext object and then converted to a DIME message if necessary.

The handler then does a little more processing, and the response is returned to the client.




Programming Microsoft. NET XML Web Services
Programming MicrosoftВ® .NET XML Web Services (Pro-Developer)
ISBN: 0735619123
EAN: 2147483647
Year: 2005
Pages: 172

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