Defining and Using SOAP Headers

From RAD Studio
Jump to: navigation, search

Go Up to Writing Servers that Support Web Services


The SOAP encoding of a request to your Web Service application and of the response your application sends include a set of header nodes. Some of these, such as the SOAP Action header, are generated and interpreted automatically. However, you can also define your own headers to customize the communication between your server and its clients. Typically, these headers contain information that is associated with the entire invokable interface, or even with the entire application, rather than just the method that is the subject of a single message.

Defining header classes

For each header you want to define, create a descendant of Soap.InvokeRegistry.TSOAPHeader. TSOAPHeader is a descendant of TRemotable. That is, SOAP header objects are simply special types of remotable objects. As with any remotable object, you can add published properties to your TSOAPHeader descendant to represent the information that your header communicates. Once you have defined a SOAP header class, it must be registered with the remotable type registry. Note that unlike other remotable classes, which are registered automatically when you register an invokable interface that uses them, you must explicitly write code to register your header types.

TSOAPHeader defines two properties that are used to represent attributes of the SOAP header node. These are MustUnderstand and Actor. When the MustUnderstand attribute is True, the recipient of a message that includes the header is required to recognize it. If the recipient can't interpret a header with the MustUnderstand attribute, it must abort the interpretation of the entire message. An application can safely ignore any headers it does not recognize if their MustUnderstand attribute is not set. The use of MustUnderstand is qualified by the Actor property. Actor is a URI that identifies the application to which the header is directed. Thus, for example, if your Web Service application forwards requests on to another service for further processing, some of the headers in client messages may be targeted at that other service. If such a header includes the MustUnderstand attribute, you should not abort the request even if your application can't understand the header. Your application is only concerned with those headers that give its URL as the Actor.

Sending and receiving headers

Once you have defined and registered header classes, they are available for your application to use. When your application receives a request, the headers on that message are automatically converted into the corresponding TSOAPHeader descendants that you have defined. Your application identifies the appropriate header class by matching the name of the header node against the type name you used when you registered the header class or against a name you supply by registering the header class with the invocation registry. Any headers for which the application can't find a match in the remotable type registry are ignored (or, if their MustUnderstand attribute is True, the application generates a SOAP fault).

You can access the headers your application receives using the Soap.InvokeRegistry.ISOAPHeaders interface. There are two ways to obtain this interface: from an instance of TInvokableClass or, if you are implementing your invokable interface without using TInvokableClass, by calling the global GetSOAPHeaders function.

Use the Get method of ISOAPHeaders to access the headers by name. For example:

TServiceImpl.GetQuote(Symbol: string): Double;
var
  Headers: ISOAPHeaders;
  H: TAuthHeader;
begin
  Headers := Self as ISOAPHeaders;
  Headers.Get(AuthHeader, TSOAPHeader(H)); { Retrieve the authentication header }
  try
    if H = nil then
      raise ERemotableException.Create("SOAP header for authentication required");
    { code here to check name and password }
  finally
    H.Free;
  end;
  { now that user is authenticated, look up and return quote }
end;

If you want to include any headers in the response your application generates to a request message, you can use the same interface. ISOAPHeaders defines a Send method to add headers to the outgoing response. Simply create an instance of each header class that corresponds to a header you want to send, set its properties, and call Send:

TServiceImpl.GetQuote(Symbol: string): Double;
var
  Headers: ISOAPHeaders;
  H: TQuoteDelay;
  TXSDuration Delay;
begin
  Headers := Self as ISOAPHeaders;
  { code to lookup the quote and set the return value }
  { this code sets the Delay variable to the time delay on the quote }
  H := TQuoteDelay.Create;
  H.Delay := Delay;
  Headers.OwnsSentHeaders := True;
  Headers.Send(H);
end;

Handling scalar-type headers

Some Web Services define and use headers that are simple types (such as an integer or string) rather than a complex structure that corresponds to a remotable type. However, Delphi's support for SOAP headers requires that you use a TSOAPHeader descendant to represent header types. You can define header classes for simple types by treating the TSOAPHeader class as a holder class. That is, the TSOAPHeader descendant has a single published property, which is the type of the actual header. To signal that the SOAP representation does not need to include a node for the TSOAPHeader descendant, call the remotable type registry's RegisterSerializeOptions method (after registering the header type) and give your header type an option of xoSimpleTypeWrapper.

Communicating the structure of your headers to other applications

If your application defines headers, you need to allow its clients to access those definitions. If those clients are also written in Delphi, you can share the unit that defines and registers your header classes with the client application. However, you may want to let other clients know about the headers you use as well. To enable your application to export information about its header classes, you must register them with the invocation registry. Registering a header class also associates that class with a header name that is defined within a namespace.

Like the code that registers your invokable interface, the code to register a header class for export is added to the initialization section of the unit in which it is defined. Use the global InvRegistry function to obtain a reference to the invocation registry and call its RegisterHeaderClass method, indicating the interface with which the header is associated:

initialization
  InvRegistry.RegisterInterface(TypeInfo(IMyWebService)); {register the interface}
  InvRegistry.RegisterHeaderClass(TypeInfo(IMyWebService), TMyHeaderClass); {and the header}
end.
static void RegTypes()
{
  // register the invokable interface:
  InvRegistry()->RegisterInterface(__delphirtti(IMyService), "", "");
// register the header to be used with it:
  InvRegistry()->RegisterHeaderClass(__delphirtti(IMyService), __classid(TMyHeader));
}
#pragma startup RegTypes 32

You can limit the header to a subset of the methods on the interface by subsequent calls to the RegisterHeaderMethod method.

Note: The implementation section's uses clause must include the InvokeRegistry unit so that the call to the InvRegistry function is defined.

Once you have registered your header class with the invocation registry, its description is added to WSDL documents when you publish your Web Service.

Note: This registration of your header class with the invocation registry is in addition to the registration of that class with the remotable type registry.

See Also