在将WSDL中定义的响应反序列化为空消息时,WCF使用SOAP会产生错误

时间:2013-10-17 01:33:53

标签: wcf web-services soap wsdl

我对WCF和相关技术很新。 (顺便说一下,使用WCF 4.0。)

以下是我需要与之交互的Web服务的WSDL文件中的一些代码段。

  <wsdl:binding name="MPGWCSTAOperations_v1_1SoapBinding" type="impl:MPGWCSTAOperations">
    <wsdlsoap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    ...
    <wsdl:operation name="MonitorStartLine">
      <wsdlsoap:operation soapAction="urn:v1_1.csta.ws.mpgw.gintel.com/MonitorStart" />
      <wsdl:input name="MonitorStartLineRequest">
        <wsdlsoap:body use="literal" />
      </wsdl:input>
      <wsdl:output name="MonitorStartLineResponse">
        <wsdlsoap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
    ...
  </wsdl:binding>

  <wsdl:portType name="MPGWCSTAOperations">
  ...
    <wsdl:operation name="MonitorStartLine" parameterOrder="monitorStartLine">
      <wsdl:input name="MonitorStartLineRequest" message="impl:MonitorStartLineRequest" />
      <wsdl:output name="MonitorStartLineResponse" message="impl:MonitorStartLineResponse" />
    </wsdl:operation>
    ....
  </wsdl:portType>

  <wsdl:message name="MonitorStartLineResponse" />

我的理解是MonitorStartLine操作被定义为返回响应消息MonitorStartLineResponse,它被定义为空消息。

我使用Visual Studio的Project - Add Service Reference工具为此生成C#代理代码。

然后我做这样的事情:

   MPGWCSTAOperationsClient cstaOperationsClient = new MPGWCSTAOperationsClient();

   MonitorStartLine monitorStartLine = new MonitorStartLine();
   monitorStartLine.pnis = new string[] {"0000032"};

   cstaOperationsClient.MonitorStartLine(monitorStartLine);

这会导致以下异常:

System.ServiceModel.CommunicationException was unhandled
  HResult=-2146233087
  Message=Error in deserializing body of reply message for operation 'MonitorStartLine'. 
  End element 'Body' from namespace 'http://schemas.xmlsoap.org/soap/envelope/' expected. 
  Found element 'monitorStartLineResponse' from namespace 'urn:v1_1.csta.ws.mpgw.gintel.com'. Line 1, position 296.
  Source=mscorlib

使用Fiddler我看到响应如下:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: Servlet 2.5; JBoss-5.0/JBossWeb-2.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 16 Oct 2013 22:01:44 GMT

149
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <monitorStartLineResponse xmlns="urn:v1_1.csta.ws.mpgw.gintel.com"/>
  </soapenv:Body>
</soapenv:Envelope>

在我看来,它符合WSDL。

我在想我可能只是忽略错误(服务器永远不会更聪明),但如果可能的话,我更愿意解决问题。

2 个答案:

答案 0 :(得分:3)

消息符合WSDL,但是在WSDL规范不明确的地方。 .NET期望Body元素内没有任何内容,因为没有定义任何消息部分。发件人正在发送一个空的monitorStartLineResponse元素,就像指定了一个消息部分一样,使用名为monitorStartLineResponse的元素。

由于WSDL规范不明确的区域,Web Services Interoperability Organization已形成。开发WS-I Basic Profile 1.1规范是为了指定WSDL的一个子集,保证跨平台可互操作。

此WSDL不符合WS-I BP 1.1。


事实上,通过阅读WSDL 1.1 specsection 3.4, soap:operation),我发现这将被视为“文档 - 文字绑定”,因为没有“风格”属性另有说法。 / p>

4.4.1, Bindings and Parts部分R2213中,WS-I BP 1.1规范说:

  

R2213 soapbind:body的parts属性值为空字符串的doc-literal描述中,相应的ENVELOPE必须在soap:Body中没有元素内容元件。

这就是.NET所期待的,但它并不是.NET所接受的。

答案 1 :(得分:3)

这是我实施的解决方法。 (关于是否更好地实现变通方法或忽略异常是有争议的 - 我更喜欢这样做。)

我正在做的是在WCF调用SOAP处理之前修改响应消息。我只是删除了WCF / SOAP认为不应该存在的XML元素。

   /// <summary>
   /// This class is used to provide a workaround for a problem due to the (censored) server sending 
   /// responses encoded in SOAP which do not, at least according to WCF standards, conform to the 
   /// WSDL specifications published for the server. 
   /// </summary>
   public class MessageInspector : IClientMessageInspector
   {
      public object BeforeSendRequest(ref Message requestMessage, IClientChannel clientChannel)
      {
         return null;  // Method not needed
      }


      public void AfterReceiveReply(ref Message replyMessage, object correlationState)
      {
         if (replyMessage.IsFault)
            return;  // Avoid distortion of SOAP fault messages

         string messageBody;

         using (MemoryStream memoryStream = new MemoryStream())
         {
            using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream))
            {
               replyMessage.WriteMessage(xmlWriter);
               xmlWriter.Flush();
               messageBody = Encoding.UTF8.GetString(memoryStream.ToArray());
            }
         }

         messageBody = messageBody.Replace(
                     "<monitorStartLineResponse xmlns=\"urn:v1_1.csta.ws.mpgw.gintel.com\" />", "");

         using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(messageBody)))
         {
            using (XmlDictionaryReader xmlDictionaryReader = 
                XmlDictionaryReader.CreateTextReader(memoryStream, new XmlDictionaryReaderQuotas()))
            {
               Message newMessage = 
                     Message.CreateMessage(xmlDictionaryReader, int.MaxValue, replyMessage.Version);
               newMessage.Properties.CopyProperties(replyMessage.Properties);
               replyMessage = newMessage;
            }
         }
      }
   }


   /// <summary>
   /// Class needed to inject the above MessageInspector class into the WCF processing of messages.
   /// </summary>
   public class InjectInspectorBehavior : IEndpointBehavior
   {
      public void Validate(ServiceEndpoint serviceEndpoint)
      {
         // Method not needed
      }

      public void AddBindingParameters(ServiceEndpoint serviceEndpoint, 
                                       BindingParameterCollection bindingParameters)
      {
         // Method not needed
      }

      public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, 
                                        EndpointDispatcher endpointDispatcher)
      {
         // Method not needed
      }

      public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime)
      {
         clientRuntime.MessageInspectors.Add(new MessageInspector());
      }
   }

这是通过添加此处显示的第二行来实现的:

  _cstaOperationsClient = new MPGWCSTAOperationsClient();
  _cstaOperationsClient.Endpoint.Behaviors.Add(new InjectInspectorBehavior());

上述代码主要基于这两个位置的代码:

http://blogs.msdn.com/b/kaevans/archive/2008/01/08/modify-message-content-with-wcf.aspx

Replacing content of WCF Message