【问题标题】:"Attribute is not declared" error in Delphi generated EWS SOAP requestDelphi 生成的 EWS SOAP 请求中的“属性未声明”错误
【发布时间】:2012-10-30 13:25:28
【问题描述】:

这是在从 Exchange Server 检索 WDSL 后使用 SoapUI 生成的调用 EWS 中的 ResolveNames 的工作示例代码:

<?xml version="1.0"?>
<soapenv:Envelope 
  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:typ="http://schemas.microsoft.com/exchange/services/2006/types" 
  xmlns:mes="http://schemas.microsoft.com/exchange/services/2006/messages">
<soapenv:Header><typ:RequestServerVersion Version="Exchange2010"/></soapenv:Header>
<soapenv:Body>
  <mes:ResolveNames ReturnFullContactData="1" SearchScope="ActiveDirectoryContacts">
    <mes:UnresolvedEntry>deve</mes:UnresolvedEntry>
  </mes:ResolveNames>
</soapenv:Body>
</soapenv:Envelope>

这是我使用的纯 Delphi XE2 代码:

procedure TFrmTestEWS.BtnConnectClick(Sender: TObject);
var
   lESB        : ExchangeServicePortType;
   lResNames   : ResolveNames;
   lReqVersion : RequestServerVersion;
   lResResult  : ResolveNamesResponse;
   lServerVer  : ServerVersionInfo;
   lUnresolved : String;
begin
   lServerVer  := ServerVersionInfo.Create;
   lResNames   := ResolveNames.Create;
   lReqVersion := RequestServerVersion.Create;
   lUnresolved := 'Deve';
   with lResNames do
   begin
      ReturnFullContactData := true;
      SearchScope           := ResolveNamesSearchScopeType.ActiveDirectoryContacts;  // Scoped enums is on!
      ParentFolderIds       := nil;
      UnresolvedEntry       := lUnresolved;
   end;
   lReqVersion.Version := ExchangeVersionType.Exchange2010;
   lESB := (HTTPRIO1 as ExchangeServicePortType);
   lESB.ResolveNames(lResNames,
                     nil,              // Impersonation
                     nil,              // MailboxCulture
                     lReqVersion,
                     lResResult,
                     lServerVer);

它生成:

<?xml version="1.0"?>
<SOAP-ENV:Envelope 
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body xmlns:NS1="http://schemas.microsoft.com/exchange/services/2006/types">
  <ResolveNames xmlns="http://schemas.microsoft.com/exchange/services/2006/messages" ReturnFullContactData="true" NS1:SearchScope="ActiveDirectoryContacts">
    <UnresolvedEntry>deve</UnresolvedEntry>
  </ResolveNames>
  <MailboxCulture xsi:nil="true"/>
  <ExchangeImpersonation xsi:nil="true"/>
  <NS1:RequestServerVersion Version="Exchange2010"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我得到的错误:

请求未通过架构验证:未声明“http://schemas.microsoft.com/exchange/services/2006/types:SearchScope”属性。

SearchScope 是 messages.xsd 中定义的属性:

<!-- ResolveNames request -->

<xs:complexType name="ResolveNamesType">
    <xs:complexContent>
        <xs:extension base="m:BaseRequestType">
            <xs:sequence>
                <xs:element name="ParentFolderIds" type="t:NonEmptyArrayOfBaseFolderIdsType" minOccurs="0"/>
                <xs:element name="UnresolvedEntry" type="t:NonEmptyStringType"/>
            </xs:sequence>
            <xs:attribute name="ReturnFullContactData" type="xs:boolean" use="required"/>
            <xs:attribute name="SearchScope" type="t:ResolveNamesSearchScopeType" default="ActiveDirectoryContacts"/>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>

<xs:element name="ResolveNames" type="m:ResolveNamesType"/>

在 types.xsd 中:

<!-- ResolveNames request -->

<xs:simpleType name="ResolveNamesSearchScopeType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="ActiveDirectory"/>
    <xs:enumeration value="ActiveDirectoryContacts"/>
    <xs:enumeration value="Contacts"/>
    <xs:enumeration value="ContactsActiveDirectory"/>
  </xs:restriction>
</xs:simpleType>

我认为 NS1:SearchScope="ActiveDirectoryContacts" 不正确,但离开 NS1: out 会出现同样的错误。
也许将类型和消息的 Exchange xmlns 规范推迟到 SOAP-ENV:Body 中是错误的原因?
此外, NS1:RequestServerVersion Version="Exchange2010" 不在 SOAP_ENV:Header 中 看起来很可疑。

我查看了some Google results,但无法让它工作。

基本上我的问题是:
如何在生成的代码中移动标签或 xmlns 属性直到它工作,而不必自己构建整个 SOAP?
如果这是不可能的,那么最好的方法是什么,以便我仍然可以从导入的类型库中受益? (this 之类的东西?)

谢谢
一月

【问题讨论】:

    标签: delphi soap delphi-xe2 exchangewebservices


    【解决方案1】:

    我决定不尝试修复特定的 XML 标记,而是完全控制输出的 SOAP 内容:
    我只是在 TStringStream 中构建 XML,然后在 HTTPRIO.BeforeExecute 中将 TStringStream 内容放入 SOAPStream。
    安装SOAPUI 后,我可以导入W​​DSL,然后“手动”生成和测试所需的SOAP 调用。完成这些后,我将它们移动到 Delphi 代码中。
    这给了我一个优势,即仍然可以访问 WDSL 生成的代码来解析结果(无需深入研究返回的 XML)。

    这是代码,它显示了旧方法 (BtnConnectClick) 和新方法 (BtnAlternateClick)。

    unit uTestEWS;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, Vcl.ExtCtrls,TypInfo, WinAPI.WinInet,
      Soap.Rio, Soap.InvokeRegistry, Soap.SOAPHTTPClient, Soap.SOAPHTTPTrans,
      services { = The file generated from the WDSL };
    
    type
      TFrmTestEWS = class(TForm)
        HTTPRIO: THTTPRIO;
        Panel1: TPanel;
        MmoLog: TMemo;
        TV: TTreeView;
        MmoResult: TMemo;
        BtnConnect: TButton;
        BtnAlternate: TButton;
        Memo1: TMemo;
        Label1: TLabel;
        procedure HTTPRIOBeforeExecute(const MethodName: string;
          SOAPRequest: TStream);
        procedure HTTPRIOAfterExecute(const MethodName: string;
          SOAPResponse: TStream);
        procedure BtnConnectClick(Sender: TObject);
        procedure HTTPRIO1HTTPWebNode1BeforePost(const HTTPReqResp: THTTPReqResp;
          Data: Pointer);
        procedure BtnAlternateClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        FSoapData: TStringStream;
        procedure Res(Msg: String);
        procedure InitializeSoapData;
        procedure FinalizeSoapData;
      public
      end;
    
    var
      FrmTestEWS: TFrmTestEWS;
    
    implementation
    
    {$R *.dfm}
    
    procedure TFrmTestEWS.BtnConnectClick(Sender: TObject);
    var
       lESB        : ExchangeServicePortType;
       lResNames   : ResolveNames;
       lReqVersion : RequestServerVersion;
       lResResult  : ResolveNamesResponse;
       lServerVer  : ServerVersionInfo;
    begin
       lServerVer  := ServerVersionInfo.Create;
       lResNames   := ResolveNames.Create;
       lReqVersion := RequestServerVersion.Create;
       lResResult  := ResolveNamesResponse.Create;
       try
          try
             // 1. Setup
    (* Replaced by alternate, see BtnAlternateClick
             lUnresolved := 'deve';
             with lResNames do
             begin
                ReturnFullContactData := true;
                SearchScope           := ResolveNamesSearchScopeType.ActiveDirectoryContacts;  // Scoped enums is on!
                ParentFolderIds       := nil;
                UnresolvedEntry       := lUnresolved;
             end;
             lReqVersion.Version := ExchangeVersionType.Exchange2010;
    *)
             // 2. Execute
             lESB := (HTTPRIO as ExchangeServicePortType);
             lESB.ResolveNames(lResNames,
                               nil,              // Impersonation
                               nil,              // MailboxCulture
                               lReqVersion,
                               lResResult,
                               lServerVer);
             // 3. Report
             MmoResult.Clear;
             Res('Server version:');
             with lServerVer do
             begin
                Res('  MajorVersion: ' + IntToStr(MajorVersion));
                Res('  MinorVersion: ' + IntToStr(MinorVersion));
                Res('  MajorBuildNumber: ' + IntToStr(MajorBuildNumber));
                Res('  MinorBuildNumber: ' + IntToStr(MinorBuildNumber));
                Res('  Version: ' + Version);
             end;
             // [ snip rest of code not relevant for this example]
          except
             on E:Exception do MmoResult.Text := E.Message;
          end;
       finally
          // 4. Clean up
          lResResult.Free;
          lServerVer.Free;
          lReqVersion.Free;
          lResNames.free;
          LESB := nil;
       end;
    end;
    
    procedure TFrmTestEWS.FormCreate(Sender: TObject);
    begin
       FSoapData := TStringStream.Create('',TEncoding.UTF8);
       FSoapData.Position := 0;
    end;
    
    procedure TFrmTestEWS.FormDestroy(Sender: TObject);
    begin
      FSoapData.Free;
    end;
    
    procedure TFrmTestEWS.HTTPRIOAfterExecute(const MethodName: string;
      SOAPResponse: TStream);
    var
       TS: TStringStream;
       S : String;
    begin
       S := MmoLog.Text + #13#10#13#10 + 'Response:' + #13#10#13#10;
       TS := TStringStream.Create(S);
       TS.Position := Length(S);
       SOAPResponse.Position := 0;
       TS.CopyFrom(SOAPResponse,SOAPResponse.Size);
       TS.Position := 0;
       MmoLog.Lines.LoadFromStream(TS);
       TS.Free;
    end;
    
    procedure TFrmTestEWS.HTTPRIOBeforeExecute(const MethodName: string;
      SOAPRequest: TStream);
    begin
       // 1. Alternate approach
       SOAPRequest.Position := 0;
       FSoapData.Position := 0;
       SOAPRequest.CopyFrom(FSoapData,FSoapData.Size);
       SOAPRequest.Size := FSoapData.Size;
       // 2. Logging
       MmoLog.Clear;
       MmoLog.Lines.Add('Request:' + #13#10#13#10);
       FSoapData.Position := 0;
       MmoLog.Lines.LoadFromStream(FSoapData);
    end;
    
    procedure TFrmTestEWS.InitializeSoapData;
    begin
       FSoapData.Clear;
       FSoapData.WriteString('<soapenv:Envelope');
       FSoapData.WriteString('  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"');
       FSoapData.WriteString('  xmlns:typ="http://schemas.microsoft.com/exchange/services/2006/types"');
       FSoapData.WriteString('  xmlns:mes="http://schemas.microsoft.com/exchange/services/2006/messages">');
       FSoapData.WriteString('<soapenv:Header><typ:RequestServerVersion Version="Exchange2010"/></soapenv:Header>');
       FSoapData.WriteString('<soapenv:Body>');
    end;
    
    procedure TFrmTestEWS.FinalizeSoapData;
    begin
       FSoapData.WriteString('</soapenv:Body>');
       FSoapData.WriteString('</soapenv:Envelope>');
    end;
    
    procedure TFrmTestEWS.BtnAlternateClick(Sender: TObject);
    begin
       InitializeSoapData;
       FSoapData.WriteString('  <mes:ResolveNames ReturnFullContactData="1" SearchScope="ActiveDirectoryContacts">');
       FSoapData.WriteString('    <mes:UnresolvedEntry>deve</mes:UnresolvedEntry>');
       FSoapData.WriteString('  </mes:ResolveNames>');
       FinalizeSoapData;
       // Pick up from first attempt 'execute': 
       BtnConnectClick(Sender);
    end;
    
    procedure TFrmTestEWS.HTTPRIO1HTTPWebNode1BeforePost(
      const HTTPReqResp: THTTPReqResp; Data: Pointer);
    const
       CONTENT_HEADER_EX2010 = 'Content-Type: text/xml; charset=utf-8';
    begin
       // http://forum.delphi-treff.de/archive/index.php/t-31817.html
       // Need to exchange the Content-Type Header, because Exchange 2010 expects
       // 'Content-Type: text/xml; charset=utf-8' instead of
       // 'Content-Type: text/xml; charset="utf-8"' which is RFC conform and used by XE2
       HttpAddRequestHeaders(Data, PChar(CONTENT_HEADER_EX2010), Length(CONTENT_HEADER_EX2010), HTTP_ADDREQ_FLAG_REPLACE);
    end;
    
    procedure TFrmTestEWS.Res(Msg: String);
    begin
       MmoResult.Lines.Add(Msg);
    end;
    
    end.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-15
      • 2020-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多