如何将 WCF 客户端配置为与托管在服务器上的第三方 WS 服务一起使用,该服务不会在回复中返回内容类型?

Posted

技术标签:

【中文标题】如何将 WCF 客户端配置为与托管在服务器上的第三方 WS 服务一起使用,该服务不会在回复中返回内容类型?【英文标题】:How to configure WCF client to work with third party WS service hosted on the server that doesn't return content-type in the reply? 【发布时间】:2011-05-09 06:10:02 【问题描述】:

如何配置 WCF 客户端以与托管在服务器上的第三方 WS 服务一起使用,该服务不会在回复中返回内容类型?

问题是配置为使用 basicHttpBinding 的此类 WCF 客户端抛出异常:“content-type is required”...

我应该使用自定义绑定还是拒绝 WCF?

附: .NET 3.5

P.P.S 消息:SOAP 消息传递需要 HTTP Content-Type 标头,但没有找到 服务器堆栈跟踪: 在 System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest 请求,HttpWebResponse 响应,HttpChannelFactory 工厂,WebException responseException)

在 System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan 超时) 在 System.ServiceModel.Channels.RequestChannel.Request(消息消息,TimeSpan 超时) 在 System.ServiceModel.Dispatcher.RequestChannelBinder.Request(消息消息,TimeSpan 超时) 在 System.ServiceModel.Channels.ServiceChannel.Call(字符串操作,布尔单向,ProxyOperationRuntime 操作,Object[] 输入,Object[] 输出,TimeSpan 超时) 在 System.ServiceModel.Channels.ServiceChannel.Call(字符串操作,布尔单向,ProxyOperationRuntime 操作,Object[] 输入,Object[] 输出) 在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime 操作) 在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage 消息)

在 [0] 处重新抛出异常: 在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg,IMessage retMsg) 在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData,Int32 类型) 在 blablabla(自定义代码)

【问题讨论】:

或者你应该“拒绝”不符合标准的第三方WS? 顺便说一句,在阅读 w3c 文档后,我有一种强烈的感觉,即内容类型标头不是 SOAP 1.1(以及 HTTP 1.1)强制要求的,并且仅在 SOAP 1.2 版本中是强制要求的......所以这是 WCF 错误,或者如果您愿意,也可以是 basicHttpBinding 功能。 【参考方案1】:

我可以说这是不可能的。我想说明原因:

非常深的内部代码(System.ServiceModel.Channels.HttpChannelFactory+HttpRequestChannel+HttpChannelRequest 类,WaitForReply 方法)——你永远不会想要用这些内部类进行继承和反射——包含两行

var response = (HttpWebResponse) this.webRequest.GetResponse(); // webRequest 是 HttpWebRequest 的类型 HttpInput 输入 = HttpChannelUtilities.ValidateRequestReplyResponse(this.webRequest, response, this.factory, responseException);

由于 HttpWebRequest 是公共的,而 GetResponse 是虚拟的,因此似乎可以通过这种方式覆盖它

public class FakedHttpWebRequest: HttpWebRequest


        protected FakedHttpWebRequest(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext)
        
        

        public override WebResponse GetResponse()
        
            WebResponse wr = base.GetResponse();
            wr.ContentType = "text/xml";
            return wr;
        

然后我们需要在某处使用反射创建 FakedHttpWebRequest 而不是 HttpWebRequest...

不幸的是,这次“某处”似乎是静态(!)方法(WebRequest.Create),因此没有机会停留在“小黑客”范围内。

【讨论】:

【参考方案2】:

我觉得我找到了如何破解它的方案。

继承新的类 System.ServiceModel.Channels.HttpChannelFactory+HttpRequestChannel+HttpChannelRequest 并覆盖 WaitForReply 虚方法

继承新的类 System.ServiceModel.Channels.HttpChannelFactory+HttpRequestChannel 并覆盖 CreateRequest(Message) 接口方法

继承新的类形式 System.ServiceModel.Channels.HttpChannelFactory 并覆盖 OnCreateChannel(EndpointAddress remoteAddress, Uri via) 方法

问题:所有这些类都是私有的和内部的(!)。解决方案,在运行时使用反射/发射/TypeBuilder/codedom 创建它们。所有提到的方法都很短(只有 WaitForReply 是冗长的)并且所有方法都是虚拟的 - 我们真的很幸运。

然后从 Http(s)TransportBindingElement 继承 并覆盖 BuildChannelFactory(BindingContext context);

然后创建自定义绑定

:)

附:我不确定是否可以创建从内部受保护类或其他程序集继承它的新类型,但我想这是可能的。

【讨论】:

不幸的是,即使使用 TypeBuilder 或 CodeDOM,也无法从另一个程序集中的内部类继承,但我会将评论保留为日志。也许我会找到一个想法,如何使用聚合或 smth 来解决这个问题。

以上是关于如何将 WCF 客户端配置为与托管在服务器上的第三方 WS 服务一起使用,该服务不会在回复中返回内容类型?的主要内容,如果未能解决你的问题,请参考以下文章

WCF 通过 MSMQ 到 WCF 请求和响应

从托管在 Linux 服务器上的 Java 客户端使用 WCF

如何在虚拟机中使用托管在 Windows 服务中的 WCF

在 WCF 自托管服务中指定 Singleton 服务

WCF与相互身份验证

托管 WCF 服务