Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 111 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
111
Dung lượng
601,79 KB
Nội dung
38-16 Developer’ s Guide Writing servers that support Web Services Defining and using SOAP headers 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 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. For more information about remotable objects, see “Using remotable objects” on page 38-6. 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. Any headers for which it 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 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. Using Web Services 38-17 Writing servers that support Web Services Use the Get method of ISOAPHeaders to access the headers by name. For example: TServiceImpl.GetQuote(Symbol: string): Double; var Headers: ISOAPHeaers; 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: ISOAPHeaers; 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. 38-18 Developer’ s Guide Writing servers that support Web Services 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. 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. 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. For information about publishing Web Services, see “Generating WSDL documents for a Web Service application” on page 38-19. 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. Creating custom exception classes for Web Services When your Web Service application raises an exception in the course of trying to execute a SOAP request, it automatically encodes information about that exception in a SOAP fault packet, which it returns instead of the results of the method call. The client application then raises the exception. By default, the client application raises a generic exception of type ERemotableExceptionwith the information from the SOAP fault packet. You can transmit additional, application-specific information by deriving an ERemotableException descendant. The values of any published properties you add to the exception class are included in the SOAP fault packet so that the client can raise an equivalent exception. To use an ERemotableException descendant, you must register it with the remotable type registry. Thus, in the unit that defines your ERemotableException descendant, you must add the InvokeRegistry unit to the uses clause and add a call to the RegisterXSClass method of the object that the global RemTypeRegistry function returns. Using Web Services 38-19 Writing servers that support Web Services If the client also defines and registers your ERemotableException descendant, then when it receives the SOAP fault packet, it automatically raises an instance of the appropriate exception class, with all properties set to the values in the SOAP fault packet. To allow clients to import information about your ERemotableException descendant, you must register it with the invocation registry as well as the remotable type registry. Add a call to the RegisterException method of the object that the global InvRegistry function returns. Generating WSDL documents for a Web Service application To allow client applications to know what Web Services your application makes available, you can publish a WSDL document that describes your invokable interfaces and indicates how to call them. To publish a WSDL document that describes your Web Service, include a TWSDLHTMLPublish component in your Web Module. (The SOAP Server Application wizard adds this component by default.) TWSDLHTMLPublish is an auto-dispatching component, which means it automatically responds to incoming messages that request a list of WSDL documents for your Web Service. Use the WebDispatch property to specify the path information of the URL that clients must use to access the list of WSDL documents. The Web browser can then request the list of WSDL documents by specifying an URL that is made up of the location of the server application followed by the path in the WebDispatch property. This URL looks something like the following: http://www.myco.com/MyService.dll/WSDL Tip If you want to use a physical WSDL file instead, you can display the WSDL document in your Web browser and then save it to generate a WSDL document file. Note In addition to the WSDL document, the THWSDLHTMLPublish also generates a WS- Inspection document to describe the service for automated tools. The URL for this document looks something like the following: http://www.myco.com/MyService.dll/inspection.wsil It is not necessary to publish the WSDL document from the same application that implements your Web Service. To create an application that simply publishes the WSDL document, omit the code that implements and registers the implementation objects and only include the code that defines and registers invokable interfaces, remotable classes that represent complex types, and any remotable exceptions. By default, when you publish a WSDL document, it indicates that the services are available at the same URL as the one where you published the WSDL document (but with a different path). If you are deploying multiple versions of your Web Service application, or if you are publishing the WSDL document from a different application than the one that implements the Web Service, you will need to change the WSDL document so that it includes updated information on where to locate the Web Service. 38-20 Developer’ s Guide Writing clients for Web Services To change the URL, use the WSDL administrator. The first step is to enable the administrator. You do this by setting the AdminEnabled property of the TWSDLHTMLPublish component to true. Then, when you use your browser to display the list of WSDL documents, it includes a button to administer them as well. Use the WSDL administrator to specify the locations (URLs) where you have deployed your Web Service application. Writing clients for Web Services You can write clients that access Web Services that you have written, or any other Web Service that is defined in a WSDL document. There are three steps to writing an application that is the client of a Web Service: • Importing the definitions from a WSDL document. • Obtaining an invokable interface and calling it to invoke the Web Service. • Processing the headers of the SOAP messages that pass between the client and the server. Importing WSDL documents Before you can use a Web Service, your application must define and register the invokable interfaces and types that are included in the Web Service application. To obtain these definitions, you can import a WSDL document (or XML file) that defines the service. The WSDL importer creates a unit that defines and registers the interfaces, headers, and types you need to use. For details on using the WSDL importer, see “Using the WSDL importer” on page 38-13. Calling invokable interfaces To call an invokable interface, your client application must include any definitions of the invokable interfaces and any remotable classes that implement complex types. If the server is written in Delphi, you can use the same units that the server application uses to define and register these interfaces and classes instead of the files generated by importing a WSDL file. Be sure that the unit uses the same namespace URI and SOAPAction header when it registers invokable interfaces. These values can be explicitly specified in the code that registers the interfaces, or it can be automatically generated. If it is automatically generated, the unit that defines the interfaces must have the same name in both client and server, and both client and server must define the global AppNameSpacePrefix variable to have the same value. Once you have the definition of the invokable interface, there are two ways you can obtain an instance to call: • If you imported a WSDL document, the importer automatically generates a global function that returns the interface, which you can then call. • You can use a remote interfaced object. Using Web Services 38-21 Writing clients for Web Services Obtaining an invokable interface from the generated function The WSDL importer automatically generates a function from which you can obtain the invokable interfaces you imported. For example, if you imported a WSDL document that defined an invokable interface named IServerInterface, the generated unit would include the following global function: function GetIServerInterface(UseWSDL: Boolean; Addr: string): IServerInterface; The generated function takes two parameters: UseWSDL and Addr. UseWSDL indicates whether to look up the location of the server from a WSDL document (true), or whether the client application supplies the URL for the server (false). When UseWSDL is false, Addr is the URL for the Web Service. When UseWSDL is true, Addr is the URL of a WSDL document that describes the Web Service you are calling. If you supply an empty string, this defaults to the document you imported. This second approach is best if you expect that the URL for the Web Service may change, or that details such as the namespace or SOAP Action header may change. Using this second approach, this information is looked up dynamically at the time your application makes the method call. Note The generated function uses an internal remote interfaced object to implement the invokable interface. If you are using this function and find you need to access that underlying remote interfaced object, you can obtain an IRIOAccess interface from the invokable interface, and use that to access the remote interfaced object: var Interf: IServerInterface; RIOAccess: IRIOAccess; X: THTTPRIO; begin Intrf := GetIServerInterface(True, 'http://MyServices.org/scripts/AppServer.dll/wsdl'); RIOAccess := Intrf as IRIOAccess; X := RIOAccess.RIO as THTTPRIO; Using a remote interfaced object If you do not use the global function to obtain the invokable interface you want to call, you can create an instance of THTTPRio for the desired interface: X := THTTPRio.Create(nil); Note It is important that you do not explicitly destroy the THTTPRio instance. If it is created without an Owner (as in the previous line of code), it automatically frees itself when its interface is released. If it is created with an Owner, the Owner is responsible for freeing the THTTPRio instance. 38-22 Developer’ s Guide Writing clients for Web Services Once you have an instance of THTTPRio, provide it with the information it needs to identify the server interface and locate the server. There are two ways to supply this information: • If you do not expect the URL for the Web Service or the namespaces and soap Action headers it requires to change, you can simply specify the URL for the Web Service you want to access. THTTPRio uses this URL to look up the definition of the interface, plus any namespace and header information, based on the information in the invocation registry. Specify the URL by setting the URL property to the location of the server: X.URL := 'http://www.myco.com/MyService.dll/SOAP/IServerInterface'; • If you want to look up the URL, namespace, or Soap Action header from the WSDL document dynamically at runtime, you can use the WSDLLocation, Service, and Port properties, and it will extract the necessary information from the WSDL document: X.WSDLLocation := 'Cryptography.wsdl'; X.Service := 'Cryptography'; X.Port := 'SoapEncodeDecode'; After specifying how to locate the server and identify the interface, you can obtain an interface pointer for the invokable interface from the THTTPRio object. You obtain this interface pointer using the as operator. Simply cast the THTTPRio instance to the invokable interface: InterfaceVariable := X as IEncodeDecode; Code := InterfaceVariable.EncodeValue(5); When you obtain the interface pointer, THTTPRio creates a vtable for the associated interface dynamically in memory, enabling you to make interface calls. THTTPRio relies on the invocation registry to obtain information about the invokable interface. If the client application does not have an invocation registry, or if the invokable interface is not registered, THTTPRio can’t build its in-memory vtable. Warning If you assign the interface you obtain from THTTPRio to a global variable, you must change that assignment to nil before shutting down your application. For example, if InterfaceVariable in the previous code sample is a global variable, rather than stack variable, you must release the interface before the THTTPRio object is freed. Typically, this code goes in the OnDestroy event handler of the form or data module: procedure TForm1.FormDestroy(Sender: TObject); begin InterfaceVariable := nil; end; The reason you must reassign a global interface variable to nil is because THTTPRio builds its vtable dynamically in memory. That vtable must still be present when the interface is released. If you do not release the interface along with the form or data module, it is released when the global variable is freed on shutdown. The memory for global variables may be freed after the form or data module that contains the THTTPRio object, in which case the vtable will not be available when the interface is released. Using Web Services 38-23 Writing clients for Web Services Processing headers in client applications If the Web Service application you are calling expects your client to include any headers in its requests or if its response messages include special headers, your client application needs the definitions of the header classes that correspond to these headers. When you import a WSDL document that describes the Web Service application, the importer automatically generates code to declare these header classes and register them with the remotable type registry. If the server is written in Delphi, you can use the same units that the server application uses to define and register these header classes instead of the files generated by importing a WSDL file. Be sure that the unit uses the same namespace URI and SOAPAction header when it registers invokable interfaces. These values can be explicitly specified in the code that registers the interfaces, or it can be automatically generated. If it is automatically generated, the unit that defines the interfaces must have the same name in both client and server, and both client and server must define the global AppSpacePrefix variable to have the same value. Note For more information about header classes, see “Defining and using SOAP headers” on page 38-16. As with a server, client applications use the ISOAPHeaders interface to access incoming headers and add outgoing headers. The remote interfaced object that you use to call invokable interfaces implements the ISOAPHeaders interface. However, you can’t obtain an ISOAPHeaders interface directly from the remote interfaced object. This is because when you try to obtain an interface directly from a remote interfaced object, it generates an in-memory vtable, assuming that the interface is an invokable interface. Thus, you must obtain the ISOAPHeaders interface from the invokable interface rather than from the remote interfaced object: var Service: IMyService; Hdr: TAuthHeader; Val: Double; begin Service := HTTPRIO1 as IService; Hdr := TAUthHeader.Create; try Hdr.Name := 'Frank Borland'; Hdr.Password := 'SuperDelphi'; (Service as ISOAPHeaders).Send(Hdr); { add the header to outgoing message } Val := Service.GetQuote('BORL'); { invoke the service } finally Hdr.Free; end; end; 38-24 Developer’ s Guide Working with sockets 39-1 Chapter 39 Chapter39 Working with sockets This chapter describes the socket components that let you create an application that can communicate with other systems using TCP/IP and related protocols. Using sockets, you can read and write over connections to other machines without worrying about the details of the underlying networking software. Sockets provide connections based on the TCP/IP protocol, but are sufficiently general to work with related protocols such as User Datagram Protocol (UDP), Xerox Network System (XNS), Digital’s DECnet, or Novell’s IPX/SPX family. Using sockets, you can write network servers or client applications that read from and write to other systems. A server or client application is usually dedicated to a single service such as Hypertext Transfer Protocol (HTTP) or File Transfer Protocol (FTP). Using server sockets, an application that provides one of these services can link to client applications that want to use that service. Client sockets allow an application that uses one of these services to link to server applications that provide the service. Implementing services Sockets provide one of the pieces you need to write network servers or client applications. For many services, such as HTTP or FTP, third party servers are readily available. Some are even bundled with the operating system, so that there is no need to write one yourself. However, when you want more control over the way the service is implemented, a tighter integration between your application and the network communication, or when no server is available for the particular service you need, then you may want to create your own server or client application. For example, when working with distributed data sets, you may want to write a layer to communicate with databases on other systems. [...]... greater detail later For now, consider the port number a numeric code for the service If you are implementing a standard service for use in cross-platform applications, Linux socket objects provide methods for you to look up the port number for the service If you are providing a new service, you can specify the associated port number in the /etc/services file (or its equivalent for your particular Linux... socket components form two types of connections: listening connections and connections to client applications The server socket receives events during the formation of each of these connections Events when listening Just before the listening connection is formed, the OnListening event occurs You can use its Handle property to make changes to the socket before it is opened for listing For example, if... controls, or ActiveForms Note COM components such as those on the ActiveX, COM+, and Servers tabs of the Component palette are not available for use in CLX applications This technology is for use on Windows only and is not cross-platform COM is a language-independent software component model that enables interaction between software components and applications running on a Windows platform The key aspect... environment With the advent of Windows 2000, this support is integrated into COM+ Transactional objects are described in detail in Chapter 46, “Creating MTS or COM+ objects.” Delphi provides wizards to easily implement applications that incorporate the above technologies in the Delphi environment For details, see “Implementing COM objects with wizards” on page 40- 19 40-2 Developer’s Guide Parts of a COM application... it waits for the reading or writing over the connection to be completed For server sockets, set the BlockMode property to bmBlocking or bmThreadBlocking to form a blocking connection Because blocking connections hold up the execution of all other code while the socket waits for information to be written or read over the connection, server socket components always spawn a new execution thread for every... the core functionality of a COM object, and a small set of API functions designed for the purpose of creating and managing COM objects When you use Delphi wizards and VCL objects in your application, you are using Delphi s implementation of the COM specification In addition, Delphi provides some wrappers for COM services for those features that it does not implement directly (such as Active Documents)... with sockets 39- 11 39- 12 Developer’s Guide Part IV Developing COM-based applications Part IV The chapters in “Developing COM-based applications” present concepts necessary for building COM-based applications, including Automation controllers, Automation servers, ActiveX controls, and COM+ applications Developing COM-based applications Chapter 40 Overview of COM technologies Chapter40 Delphi provides... common for computers to refer to themselves with the name localhost and the IP number 1 27. 0.0.1 39- 4 Developer’s Guide Describing sockets Server sockets do not need to specify a host The local IP address can be read from the system If the local system supports more than one IP address, server sockets will listen for client requests on all IP addresses simultaneously When a server socket accepts a connection,... providing a new service, you can specify the associated port number in the /etc/services file (or its equivalent for your particular Linux distribution) See your Linux documentation for more information 39- 2 Developer’s Guide Types of socket connections Types of socket connections Socket connections can be divided into three basic types, which reflect how the connection was initiated and what the local... way for clients to ask a COM component which features it supports at runtime To provide additional features for your component, you simply add an additional interface for those features Applications can access the interfaces of COM components that exist on the same computer as the application or that exist on another computer on the network using a mechanism called Distributed COM (DCOM) For more information . later. For now, consider the port number a numeric code for the service. If you are implementing a standard service for use in cross-platform applications, Linux socket objects provide methods for. listening Just before the listening connection is formed, the OnListening event occurs. You can use its Handle property to make changes to the socket before it is opened for listing. For example,. { invoke the service } finally Hdr.Free; end; end; 38-24 Developer’ s Guide Working with sockets 39- 1 Chapter 39 Chapter 39 Working with sockets This chapter describes the socket components that