首页 > 解决方案 > 为自定义响应标头创建 wsdl

问题描述

我必须调用第三方 SOAP Web 服务。我正在使用 c#、Visual Studio 和 WCF。供应商无法为我提供 wsdl,所以我自己编写,然后使用我创建的 wsdl 添加服务引用。

这是一个示例请求:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <AuthHeader xmlns="http://sample.com/">
      <Username>...</Username>
      <Password>...</Password>
    </AuthHeader>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <GetAttachment xmlns="http://sample.com/">
      <AttachmentID >4851888</AttachmentID>
    </Get>
  </s:Body>
</s:Envelope>

这是一个示例响应:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <StatusType xmlns="http://sample.com/">
      <StatusNumber>0</StatusNumber>
      <Description>Success</Description>
    </StatusType>
  </soap:Header>
  <soap:Body>
    <GetAttachmentResponse xmlns="http://sample.com/">
      {
      ..json content
      }
    </GetAttachmentResponse>
  </soap:Body>
</soap:Envelope>

我创建的 wsdl 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
                  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
                  xmlns:tns="http://sample.com/"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:s="http://www.w3.org/2001/XMLSchema"
                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
                  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
                  targetNamespace="http://sample.com/"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:types>
      <s:schema elementFormDefault="qualified" targetNamespace="http://sample.com/">

      <s:element name="AuthHeader" type="tns:AuthHeader" />      
      <s:complexType name="AuthHeader">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="Username" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Password" type="s:string" />
        </s:sequence>
        <s:anyAttribute />
      </s:complexType>

      <s:element name="GetAttachment">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="AttachmentID" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>

      <s:element name="StatusHeader" type="tns:StatusHeader" />
      <s:complexType name="StatusHeader">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="StatusNumber" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
        </s:sequence>
        <s:anyAttribute />
      </s:complexType>

      <s:element name="GetAttachmentResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="Attachment">
              <s:complexType>
                <s:sequence>
                  <s:element minOccurs="0" maxOccurs="1" name="mimetype" type="s:string" />
                  <s:element minOccurs="0" maxOccurs="1" name="filename" type="s:string" />
                  <s:element minOccurs="0" maxOccurs="1" name="content" type="s:base64Binary" />
                  <s:element minOccurs="0" maxOccurs="1" name="description" type="s:string" />                  
                </s:sequence>
              </s:complexType>
            </s:element>
          </s:sequence>
        </s:complexType>
      </s:element>

        </s:schema>
    </wsdl:types>

  <wsdl:message name="GetAttachmentSoapIn">
    <wsdl:part name="parameters" element="tns:GetAttachment" />
  </wsdl:message>

  <wsdl:message name="GetAttachmentSoapOut">
    <wsdl:part name="response" element="tns:GetAttachmentResponse" />
  </wsdl:message>

  <wsdl:message name="GetAttachmentAuthenticationHeader">
    <wsdl:part name="AuthenticationHeader" element="tns:AuthHeader" />
  </wsdl:message>

  <wsdl:message name="GetAttachmentStatusHeader">
    <wsdl:part name="StatusHeader" element="tns:StatusHeader" />
  </wsdl:message>


    <wsdl:portType name="AttachmentsSOAP">
        <wsdl:operation name="GetAttachment">
            <wsdl:input message="tns:GetAttachmentSoapIn"/>
            <wsdl:output message="tns:GetAttachmentSoapOut"/>
        </wsdl:operation>
    </wsdl:portType>

    <wsdl:binding name="AttachmentsSOAP" type="tns:AttachmentsSOAP">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="GetAttachment">
            <soap:operation soapAction=""/>
      <wsdl:input>
        <soap:body use="literal" />
        <soap:header message="tns:GetAttachmentAuthenticationHeader" part="AuthenticationHeader" use="literal" />
      </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
        <soap:header message="tns:GetAttachmentStatusHeader" part="StatusHeader" use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>

    <wsdl:service name="ApplyV1">
        <wsdl:port name="AttachmentsSOAP" binding="tns:AttachmentsSOAP">
            <soap:address location="https://api.sample.com/service"/>
        </wsdl:port>    
    </wsdl:service>

</wsdl:definitions>

当我添加服务引用时,生成的代理类包含我期望的 GetAttachment 函数。问题是响应标头作为函数返回类型返回,soap 信封的实际响应(主体)作为输出参数返回:

public AttachmentAPI.StatusHeader GetAttachment(AttachmentAPI.AuthHeader AuthHeader, AttachmentAPI.GetAttachment GetAttachment1, out AttachmentAPI.GetAttachmentResponse GetAttachmentResponse) {...}

我可以调用 GetAttachment 函数,它正确地进行了一次肥皂调用。soap 服务返回一个结果,并将结果反序列化为 GetAttachmentResponse 对象,而不是 StatusHeader 对象。理想情况下,签名看起来像......

public AttachmentAPI.GetAttachmentResponse GetAttachment(AttachmentAPI.AuthHeader AuthHeader, AttachmentAPI.GetAttachment GetAttachment) {...}

...其中 AttachmentAPI.GetAttachmentResponse 包含响应正文和自定义响应标头。任何帮助表示赞赏。

标签: c#asp.netwcfsoapwsdl

解决方案


我解决了这个问题,主要是通过将正确的部分放入消息中,并从 portType 和绑定中引用消息。这确实起作用,尽管它仍然会导致 Visual Studio 生成一个代理类,我认为这是一个不受欢迎的函数签名。header 对象从函数返回,响应体反序列化的对象作为 out 参数返回:

public AttachmentAPI.StatusResponseHeaderType GetAttachment(AttachmentAPI.AuthRequestHeaderType AuthHeader, AttachmentAPI.GetAttachment GetAttachment1, out AttachmentAPI.GetAttachmentResponse GetAttachmentResponse) {...}

但至少它起作用了,我可以检索状态标题和实际的正文内容。

这是我修改后的wsdl:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
                  xmlns:tns="http://sample.com/"
                    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                    xmlns:s="http://www.w3.org/2001/XMLSchema"
                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
                  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
                    targetNamespace="http://sample.com/"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://sample.com/">

      <s:element name="AuthHeader" type="tns:AuthRequestHeaderType" />

      <s:complexType name="AuthRequestHeaderType">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="Username" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Password" type="s:string" />
        </s:sequence>
        <s:anyAttribute />
      </s:complexType>

      <s:element name="StatusType" type="tns:StatusResponseHeaderType" />

      <s:complexType name="StatusResponseHeaderType">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="StatusNumber" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
        </s:sequence>
        <s:anyAttribute />
      </s:complexType>


      <s:element name="GetAttachment">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="AttachmentID" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>

      <s:element name="GetAttachmentResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="Attachment">
              <s:complexType>
                <s:sequence>
                  <s:element minOccurs="0" maxOccurs="1" name="mimetype" type="s:string" />
                  <s:element minOccurs="0" maxOccurs="1" name="filename" type="s:string" />
                  <s:element minOccurs="0" maxOccurs="1" name="content" type="s:base64Binary" />
                  <s:element minOccurs="0" maxOccurs="1" name="description" type="s:string" />
                </s:sequence>
              </s:complexType>
            </s:element>
          </s:sequence>
        </s:complexType>
      </s:element>

    </s:schema>
  </wsdl:types>

  <wsdl:message name="GetAttachmentSoapIn">
    <wsdl:part name="AuthenticationHeader" element="tns:AuthHeader" />
    <wsdl:part name="parameters" element="tns:GetAttachment"   />
  </wsdl:message>

  <wsdl:message name="GetAttachmentSoapOut">
    <wsdl:part name="StatusHeader" element="tns:StatusType" />
    <wsdl:part name="response" element="tns:GetAttachmentResponse" />
  </wsdl:message>

  <!--<wsdl:message name="GetAttachmentAuthenticationRequestHeaderMessage">
    <wsdl:part name="AuthenticationHeader" element="tns:AuthHeader" />
  </wsdl:message>

  <wsdl:message name="GetAttachmentStatusResponseHeaderMessage">
    <wsdl:part name="StatusHeader" element="tns:StatusHeader" />
  </wsdl:message>-->

  <!-- PortType defines the abstract interface of a web service. 
       Port type is implemented by the binding and service elements -->
  <wsdl:portType name="AttachmentsSoap">
    <wsdl:operation name="GetAttachment">
      <wsdl:input message="tns:GetAttachmentSoapIn"/>
      <wsdl:output message="tns:GetAttachmentSoapOut"/>
    </wsdl:operation>
  </wsdl:portType>

  <!-- the binding specifies concrete implementation details and 
        essentially maps a portType to a set of protocols (HTTP and SOAP) 
        message styles (Document/RPC) and encodings (literal) -->
  <wsdl:binding name="AttachmentsSoap" type="tns:AttachmentsSoap">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="GetAttachment">
      <soap:operation soapAction=""/>
      <wsdl:input>
        <soap:header message="tns:GetAttachmentSoapIn" part="AuthenticationHeader" use="literal" />
        <soap:body use="literal" parts="parameters" />
      </wsdl:input>
      <wsdl:output>
        <soap:header message="tns:GetAttachmentSoapOut" part="StatusHeader" use="literal" />
        <soap:body use="literal" parts="response"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>

  <wsdl:service name="ApplyV1">
    <wsdl:port name="AttachmentsSoap" binding="tns:AttachmentsSoap">
      <soap:address location="https://api.sample.com/soap/apply/v1"/>
    </wsdl:port>
  </wsdl:service>

</wsdl:definitions>

推荐阅读