In the previous example (see "Delphi 6 preemptive research-one of bizsnap/soap/WebService-A Hello world! In the example), we can see that you can easily call remote objects through soap, although the object used in this example is a Delphi class, but in fact, you only need to make a soap package for the object to call various objects, including COM, CORBA, and EJB (except that EJB must be implemented in Java, COM/CORBA can be implemented using Delphi ). In that example, the data types used by the interface methods are all standard types, but in actual applications, the user-defined type is often transferred, and the operation is slightly troublesome, for details, see the example in Li Wei's article "interesting and unlimitedly new technology-web service. Similarly, here we will also use an example to illustrate how to pass custom data types through soap. This example will be a troublesome example: Server: 1. New | WebServices | SOAP server application, for example: This example uses the web app debugger (For details, refer to Delphi 6 preemptive research-web application development and debugging) and sets its coclass name to wadsoapdemo2, for example: 2. saveall, unit2 is named svrwmmain, unit1 is not renamed, and project1 is named server; 3. New | data module, which is saved as svrdatamod; 4. Add two dbexpress controls: sqlconnection1 and sqldataset1, for example: Its attribute is set:
Sqlconnection1 |
Connectionname: = iblocal; Loginprompt: = false; Params. Values ['database']: = '[...]/examples/database/employee. GDB '; // The above [...] is your Interbase installation path |
Sqldataset1 |
Sqlconnection: = sqlconnection1; Commandtext: = 'select full_name, phone_ext from employee where emp_no =: emp_no '; |
5. New | unit: Save the Unit as svrdatatype. Its content is as follows: unit SvrDataType;interfaceUses InvokeRegistry;Type TEmpInfo = Class( TRemotable ) Private FName : String; FPhone : String; published Property Name : String Read FName Write FName; Property Phone : String Read FPhone Write FPhone; end;implementationInitialization RemClassRegistry.RegisterXSClass( TEmpInfo );Finalization RemClassRegistry.UnRegisterXSClass( TEmpInfo );end. This Unit defines the class: tempinfo, which is used to record employee information, including the name and phone fields, all of which are string types. The data type to be passed to the client must be derived from the tremotable class, which can automatically process the transfer of type information. If you want to manually process the transmission of custom data types, you must derive from the tremotablexs class. Its usage is similar to that of tremotable. However, you must implement two conversion methods: nativetoxs and xstonative, for details, see DELPHI6/source/soap/xsbuiltins. implementation of several classes in PAS. It should be noted that the two attributes in this class should be placed in published. This must be done here. I once put them in public, so that the client cannot obtain the data type information of the server, it was later discovered that they must be placed in published, so although this is not a control, these properties are not to be displayed in the object inspector, but still need to be placed in published. This may be because published has more rtti (Run time type info, runtime type information) than public, and Remote Data Type depends on rtti. Finally, register and unregister the class in the remote Class Registration Information Library. 6. New | unit: Save the Unit as svrsoapintf. Its content is as follows: unit SvrSoapIntf;interfaceUses InvokeRegistry, SvrDataType;Type ISoapEmployee = Interface( IInvokable ) ['{31903B5A-96B3-43C2-A7B5-F67F6DB829E5}'] Function GetEmployee( aEmpNo : Integer ) : TEmpInfo; StdCall; End;implementationInitialization InvRegistry.RegisterInterface( TypeInfo( ISoapEmployee ) );end. The soap interface is defined in this unit, which is no big difference from the previous example. This is just to be clear, put this interface in a separate unit for implementation. The only difference is that the getemployee method in this interface returns a custom data type: tempinfo. 7. Add the SOAP implementation class to the svrwmmain unit. The complete unit content is as follows: unit SvrWMMain;interfaceuses SysUtils, Classes, HTTPApp, WSDLPub, SOAPPasInv, SOAPHTTPPasInv, SoapHTTPDisp, WebBrokerSOAP;type TWebModule2 = class(TWebModule) HTTPSoapDispatcher1: THTTPSoapDispatcher; HTTPSoapPascalInvoker1: THTTPSoapPascalInvoker; WSDLHTMLPublish1: TWSDLHTMLPublish; private { Private declarations } public { Public declarations } end;var WebModule2: TWebModule2;implementationuses WebReq, InvokeRegistry, SvrDataType, SvrSoapIntf, SvrDataMod;{$R *.DFM}Type TSoapEmployee = class( TInvokableClass, ISoapEmployee ) Protected Function GetEmployee( aEmpNo : Integer ) : TEmpInfo; StdCall; End;{ TSoapEmployee }Function TSoapEmployee.GetEmployee(aEmpNo: Integer): TEmpInfo; StdCall;Begin Result := TEmpInfo.Create; If ( Not Assigned( DataModule2 ) ) Then DataModule2 := TDataModule2.Create( Nil ); Try DataModule2.SQLConnection1.Open; With DataModule2.SQLDataSet1 Do Begin ParamByName( 'EMP_NO' ).AsInteger := aEmpNo; Open; If ( Not Eof ) Then Begin Result.Name := FieldByName( 'FULL_NAME' ).AsString; Result.Phone := FieldByName( 'PHONE_EXT' ).AsString; End Else Begin Result.Name := ''; Result.Phone := ''; End; Close; End; DataModule2.SQLConnection1.Close; Finally DataModule2.Free; DataModule2 := Nil; End;End;initialization WebRequestHandler.WebModuleClass := TWebModule2; InvRegistry.RegisterInvokableClass( TSoapEmployee );end. The implementation class tsoapemployee of the interface is defined and implemented in a similar way as in the previous example. The implementation of getemployee is not complex: first, if you have not created an instance of datamodule2 (you need to remove datamodule2 from the automatic creation list in project | options), create a datamodule2 instance; connect to the database, query the employee information of the specified employee number, and return the information. Note: dbexpress is used here. In some cases, it is not the same as Bde/ADO. If recordcount is not used, you can only use EOF to determine whether a query result exists. 8. At this point, all the programs on the server are completed, compiled and run, and then exited to complete registration of the web app debugger application. Start the web app debugger, and then start the browser. in the address bar, enter http: // localhost: 1024/server. wadsoapdemo2/WSDL/isoapemployee can view its WSDL content, which contains necessary information about the custom type. However, if the attributes of the tempinfo class in the svrdatatype unit are not included in the published section, the type information is not displayed here. Below is part of the types tag in the WSDL: <types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:SvrDataType"> <xs:complexType name="TEmpInfo"> <xs:sequence> <xs:element name="Name" type="xs:string"/> <xs:element name="Phone" type="xs:string"/> </xs:sequence> </xs:complexType></xs:schema> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:WSDLSoap"> <xs:complexType name="TWSDLSOAPPort"> <xs:sequence> <xs:element name="PortName" type="xs:string"/> <xs:element name="Addresses" type="ns3:TWideStringDynArray"/> </xs:sequence> </xs:complexType></xs:schema> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:Types"> <xs:complexType name="TWideStringDynArray"> <xs:complexContent> <xs:restriction base="soapenc:Array"> <xs:sequence/> <xs:attribute ref="soapenc:arrayType" n1:arrayType="xs:string[]"xmlns:n1="http://schemas.xmlsoap.org/wsdl/"/> </xs:restriction> </xs:complexContent> </xs:complexType></xs:schema> </types> From the above section of WSDL, we can see that the server has exported three "complex types" -- complextype: tempinfo, twsdlsoapport, and twidestringdynarray. In addition to tempinfo, the other two are the types defined in Delphi. We will see them again when the client imports the WSDL. Let's look at the implementation of the client: 1. New | application creates a common VCL application; 2. saveall, unit1 is named clnmain, and project1 is named client; 3. Put httprio1, edit1, button1, label1, label2, and other controls on form1, such: The text of edit1 is set to 1, the caption of button1 is set to getemployee, And the URL attribute of httprio1 is set to http: // localhost: 1024/server. wadsoapdemo2/soap; 4. New | Web Services importer. Similar to the previous example, the imported URL is changed to http: // localhost: 1024/server. wadsoapdemo2/WSDL/isoapemployee; 5. if the server-side WSDL is as described above, three units will be imported, including twsdlsoapport, tempinfo, and isoapemployee. Among them, isoapemployee is the soap interface unit we know, tempinfo is the data type defined on the server, and twsdlsoapport is a data type defined in Delphi. We have seen this type in the server's WSDL. Save all: Save the twsdlsoapport unit as clnsoapport, tempinfo as clndatatype, and isoapemployee as clnsoapintf. Note that you should change the two units named unitn in the uses in the clnsoapintf unit to clnsoapport and clndatatype. The contents of these three units do not need to be changed, as long as the server is correct, you do not need to know the content of these three units (especially the corresponding units of clnsoapintf and clndatatype are basically the same as those of the server), so their contents are not listed here. 6. Double-click button1 and enter the following code: procedure TForm2.Button1Click(Sender: TObject);Var ei : TEmpInfo;begin ei := ( HTTPRIO1 As ISoapEmployee ).GetEmployee( StrToInt( Edit1.Text ) ); If ( Assigned( ei ) ) Then Begin Label1.Caption := ei.Name; Label2.Caption := ei.Phone; End;end; 7. compile and run. In edit1, enter "1" or employee numbers that do not have corresponding records in other databases. Press button1, label1, and label2; enter the employee number recorded in "2" or other databases, the full name of the employee is displayed in label1, and the phone number of the employee is displayed in label2, for example: It is not that complicated to read this example again. Jun.20-01, Oct.20, oct.24 |