2015-01-22 2 views
3

간단한 세관 신고 확인을 위해 Delphi 2007에서 SOAP 클라이언트를 작성합니다. 나는 SOAP 서버에 몇 가지 정보를 보내고 서버가 내가 보낸 정보를 찾을 수 없다면 세관 릴리스 또는 SOAP 오류에 대한 세부 사항을 다시 받아야한다. 첫 번째 부분은 잘 작동하지만 오류 처리는하지 않습니다.Delphi 2007 SOAP 오류 처리

<?xml version="1.0" encoding="ISO-8859-1"?> 
<xsd:schema targetNamespace="http://trips.crownagents.com/wsexception/message" 
      xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      xmlns="http://trips.crownagents.com/wsexception/message"> 
    <xsd:element name="WSException" type="WSException" nillable="true"/> 
    <xsd:complexType name="WSException"> 
    <xsd:sequence> 
     <xsd:element name="ErrorCode" type="xsd:string" minOccurs="0" maxOccurs="1"/> 
     <xsd:element name="ErrorDescription" type="xsd:string" minOccurs="0" maxOccurs="1"/> 
     <xsd:element name="Stack" type="xsd:string" minOccurs="0" maxOccurs="1"/> 
    </xsd:sequence> 
    </xsd:complexType> 
</xsd:schema> 

을 그리고 내가 돌아올 SOAP 응답은 예외 참조 할 것 :

<?xml version="1.0" encoding="UTF-8"?> 
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:ns0="http://trips.crownagents.com/wsexception/message" 
       xmlns:ns1="http://trips.crownagents.com/external/customs/release/message" 
       xmlns:ns2="http://trips.crownagents.com/external/common/message"> 
    <env:Body> 
    <env:Fault xsi:type="env:Fault"> 
     <faultcode>env:Server</faultcode> 
     <faultstring xsi:nil="1"/> 
     <detail> 
     <ans1:WSExceptionResponse xmlns:ans1="http://msgsvr.trips.crownagents.com/"> 
      <ErrorCode>0002</ErrorCode> 
      <ErrorDescription>Invalid Declaration</ErrorDescription> 
      <Stack>getSingleResult() did not retrieve any entities.</Stack> 
     </ans1:WSExceptionResponse> 
     </detail> 
    </env:Fault> 
    </env:Body> 
</env:Envelope> 
을 - (전체 WSDL이 표시되지 않습니다 메인 WSDL에 의해 포함)를 WSDL 사용자 지정 SOAP 예외를 지정합니다

그러나 내 코드에는 WSExceptionResponse가 표시되지 않습니다. 델파이 2007 (https://groups.google.com/forum/#!msg/borland.public.delphi.webservices.soap/71t3P-vPMbk/qw9JVTEVS3YJ)에서 SOAP 처리에 문제가 몇이 있음을 나는 읽고

Try 
    Res := Rel.releaseStatus(RelInfo); 
Except 
    On E: WSExceptionResponse Do // This never fires 
    Status('Release check error (' + E.ErrorCode + ' - ' + 
      E.ErrorDescription + ').', True); 
    Else 
    Status('Release check error (' + Exception(ExceptObject).Message + 
      ').', True); 
End; 

내가 제안에 따라 그것을 되돌릴 OPToSOAPDomConv.pas 파일을 변경 한 대신, 나는 일반적인 ERemotableException를 얻을 수 그러나 그것은 도움이되지 않습니다. 누구든지 내가 뭘 잘못하고 있는지에 대한 아이디어가 있습니까?

+0

SoapUI를 사용하여 서비스가 예상대로 작동하는지 확인한 다음 SoapUI 및 Delphi SOAP 요청간에 차이점이 있는지 확인하십시오. – mjn

+0

SoapUI가 메시지를 보내는 방법은 델파이가하는 방법과는 많이 다르지만 (네임 스페이스와 관련이 있지만) 반환 된 폴트 메시지는 정확히 같은 방법입니다. – Caynadian

답변

3

Delphi 2007을 여전히 사용하고있는 사용자 중이 문제를 해결 한 방법은이 문제를 해결 한 것입니다.

먼저 프로젝트 디렉토리에있는 Delphi 소스 디렉토리 (\ Program Files < (x86)> \ CodeGear \ RAD Studio \ 5.0 \ source \ Win32 \ soap)에서 OPToSOAPDomConv.pas 및 InvokeRegistry.pas를 복사하십시오. 소스를 사용자 정의 할 때이 두 파일을 프로젝트에 추가하면 Delphi와 함께 제공되는 미리 컴파일 된 DCU를 사용하는 대신 프로젝트와 함께 다시 컴파일해야합니다. OPToSOAPDomConv.pas 파일에서

의 ProcessFault 절차를 찾아로 교체 다음

procedure TOPToSoapDomConvert.ProcessFault(FaultNode: IXMLNode); 
var 
    FA, FC, FD, FS, CustNode: IXMLNode; 
    I, J: Integer; 
    AMessage: WideString; 
    AClass: TClass; 
    URI, TypeName: WideString; 
    Count: Integer; 
    PropList: PPropList; 
    Ex: ERemotableException; 

    function GetNodeURIAndName(const Node: IXMLNode; var TypeURI, 
    ElemName: InvString): boolean; 
    var 
    Pre: InvString; 
    begin 
    ElemName := Node.NodeName; 
    if IsPrefixed(ElemName) then 
    begin 
     Pre := ExtractPrefix(ElemName); 
     ElemName := ExtractLocalName(ElemName); 
     TypeURI := Node.FindNamespaceURI(Pre); 
    end 
    else 
     TypeURI := Node.NamespaceURI; 
    Result := True; 
    end; 

begin 
    FA := nil; 
    FC := nil; 
    FD := nil; 
    FS := nil; 
    Ex := nil; 
    for I := 0 to FaultNode.ChildNodes.Count - 1 do 
    begin 
    if  SameText(ExtractLocalName(FaultNode.ChildNodes[I].NodeName), SSoapFaultCode) then 
     FC := FaultNode.ChildNodes[I] 
    else if SameText(ExtractLocalName(FaultNode.ChildNodes[I].NodeName), SSoapFaultString) then 
     FS := FaultNode.ChildNodes[I] 
    else if SameText(ExtractLocalName(FaultNode.ChildNodes[I].NodeName), SSoapFaultDetails) then 
     FD := FaultNode.ChildNodes[I] 
    else if SameText(ExtractLocalName(FaultNode.ChildNodes[I].NodeName), SSoapFaultActor) then 
     FA := FaultNode.ChildNodes[I]; 
    end; 

    { Retrieve message from FaultString node } 
    if FS <> nil then 
    AMessage := FS.Text; 

    { If there's a <detail> node, try to map it to a registered type } 
    if FD <> nil then 
    begin 
    { Some SOAP stacks, including Delphi6 and others (see 
     http://softwaredev.earthweb.com/script/article/0,,12063_641361_2,00.html) 
     use the approach of putting custom fault info right at the <detail> node: 

     Listing 4 - Application Fault Details 
     <SOAP-ENV:Fault> 
     <faultcode>300</faultcode> 
     <faultstring>Invalid Request</faultstring> 
     <runcode>1</runcode> 
     <detail xmlns:e="GetTemperatureErr-URI" 
       xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" 
       xsi:type="e:GetTemperatureFault"> 
      <number>5575910</number> 
      <description>Sensor Failure</description> 
      <file>GetTemperatureClass.cpp</file> 
      <line>481</line> 
     </detail> 
     </SOAP-ENV:Fault> 

     However, much more common is the approach where the type and namespace 
     are on the childnode of the <detail> node. Apache, MS and the SOAP spec. 
     seem to lean towards that approach: 

     Example 10 from the SOAP 1.1 Spec: 

     <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> 
     <SOAP-ENV:Body> 
      <SOAP-ENV:Fault> 
       <faultcode>SOAP-ENV:Server</faultcode> 
       <faultstring>Server Error</faultstring> 
       <detail> 
        <e:myfaultdetails xmlns:e="Some-URI"> 
         <message>My application didn't work</message> 
         <errorcode>1001</errorcode> 
        </e:myfaultdetails> 
       </detail> 
      </SOAP-ENV:Fault> 
     </SOAP-ENV:Body> 
     </SOAP-ENV:Envelope> 

     For interop reasons we favor the later approach but we'll support both here!! 
    } 
    CustNode := nil; 
    if GetElementType(FD, URI, TypeName) then 
     CustNode := FD 
    else 
    begin 
     if ntElementChildCount(FD) > 0 then 
     begin 
     CustNode := ntElementChild(FD, 0); 
     if not GetElementType(CustNode, URI, TypeName) and 
      not GetNodeURIAndName(CustNode, URI, TypeName) then 
      CustNode := nil; 
     end; 
    end; 

    if (CustNode <> nil) then 
    begin 
     AClass := RemClassRegistry.URIToClass(URI, TypeName); 
     if AClass <> nil then 
     begin 
     if AClass.InheritsFrom(ERemotableException) then 
     begin 
      Ex := ERemotableExceptionClass(AClass).Create(AMessage); 
      LoadObject(Ex, FaultNode, CustNode); 
     end; 
     end; 
    end; 
    end; 

    { Create default SOAP invocation exception if no suitable class was found } 
    if Ex = nil then 
    Ex := ERemotableException.Create(AMessage); 
    if FA <> nil then 
    Ex.FaultActor := FA.Text; 
    if FC <> nil then 
    Ex.FaultCode := FC.Text; 
    if FD <> nil then 
    Ex.FaultDetail := FD.XML; 
    raise Ex; 
end; 

다음, GetElementType 함수에 다음으로 교체 : 마지막으로

function TSOAPDomConv.GetElementType(Node: IXMLNode; var TypeURI, TypeName: InvString): Boolean; 
var 
    Idx: Integer; 
    S : InvString; 
    V: Variant; 
    Pre: InvString; 
begin 
    TypeURI := ''; 
    TypeName := ''; 
    Result := False; 
    if (Node.NamespaceURI = SSoap11EncodingS5) and 
    (Node.LocalName = SSoapEncodingArray) then 
    begin 
    TypeURI := SSoap11EncodingS5; 
    TypeName := SSoapEncodingArray; 
    Result := True; 
    end 
    else 
    begin 
    V := GetTypeBySchemaNS(Node, XMLSchemaInstNameSpace); 
    if VarIsNull(V) then 
     V := Node.GetAttribute(SSoapType); 
    if not VarIsNull(V) then 
    begin 
     S := V; 
     Idx := Pos(':', S); { do not localize } 
     if Idx <> 0 then 
     begin 
     TypeName := Copy(S, Idx + 1, High(Integer)); 
     Pre := Copy(S, 1, Idx - 1); 
     TypeURI := Node.FindNamespaceURI(Pre); 
     end 
     else 
     begin 
     TypeName := S; 
     TypeURI := ''; 
     end; 
     Result := True; 
    end; 
    end 
end; 

, InvokeRegistry.pas 파일을 열고 GetExternalPropName 함수를 찾으십시오. 이것에

if Info.Kind = tkClass then 

: 말한다 라인 변경

if (Info.Kind = tkClass) and Assigned(GetTypeData(info).ParentInfo) then 

컴파일하고 응용 프로그램을 실행하고 당신이 잘되어야합니다.

이 모든 크레딧은이 스레드 http://www.codenewsfast.com/cnf/article/859054074/permalink.art-ng1920q2368과이 http://www.delphigroups.info/2/7/342954.html의 사용자에게 제공됩니다.