我对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。
我在想我可能只是忽略错误(服务器永远不会更聪明),但如果可能的话,我更愿意解决问题。
答案 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 spec(section 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