为什么我的 WCF 代理发送 JSON 和 Content-Type: application/xml?
Why is my WCF proxy sending JSON with Content-Type: application/xml?
总结
我在 Azure 应用程序服务中有一个 WCF 后端服务 运行,我正在开发一个代理,它将使用相同的接口,但会处理一些消息以与客户端系统兼容。我知道这有点含糊,但细节并不重要。
问题是,当来自 Visual Studio 17 的 IIS Express 中的代理 运行 调用后端时,它这样做是不正确的,并且后端拒绝了请求。 (奇怪的是,拒绝的确切性质在真实服务和我为此 post 创建的最小可重现测试用例之间有所不同,但差异已经足够晚了,我认为它不相关)。
通过向 System.Net
添加跟踪侦听器,我可以观察到代理对后端的调用在 JSON 中有一个 body,但是 header 声称它是 XML:
System.Net Verbose: 0 : [4140] Data from ConnectStream#29742526::Write
System.Net Verbose: 0 : [4140] 00000000 : 7B 22 42 61 72 22 3A 22-42 2D 50 72 6F 78 69 65 : {"Bar":"B-Proxie
System.Net Verbose: 0 : [4140] 00000010 : 64 22 2C 22 46 6F 6F 22-3A 22 41 22 7D : d","Foo":"A"}
System.Net Verbose: 0 : [4140] Exiting ConnectStream#29742526::Write()
System.Net Verbose: 0 : [4140] Entering ConnectStream#29742526::Close()
System.Net Verbose: 0 : [4140] Exiting ConnectStream#29742526::Close()
System.Net Verbose: 0 : [4140] Entering HttpWebRequest#45658036::GetResponse()
System.Net Information: 0 : [4140] HttpWebRequest#45658036 - Request: POST /DemoService.svc/DoTheThing HTTP/1.1
System.Net Information: 0 : [4140] ConnectStream#29742526 - Sending headers
{
Content-Type: application/xml; charset=utf-8
Host: wcfproxydemo.azurewebsites.net
Content-Length: 29
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Close
}.
我可以更改一些配置以使其发送 JSON 和正确的 Content-Type header 吗? 我不想这样做必须将所有内容切换到 XML.
详情
我的最小可重现测试用例是 available on Github。我唯一省略的是将后端推送到 Azure 的发布配置文件。
各个项目共享一个接口项目,没有auto-generated服务引用。 (这可能是一种可能的前进方式,但似乎 heavy-weight,所以我更喜欢 config-based 解决方案)。三个组件的 WCF 配置是
后端
<system.serviceModel>
<bindings>
<customBinding>
<binding name="rest-https-nokeepalive">
<webMessageEncoding />
<httpsTransport manualAddressing="true" allowCookies="false" keepAliveEnabled="false" maxBufferSize="10000000" maxReceivedMessageSize="10000000" maxBufferPoolSize="10000000" />
</binding>
</customBinding>
</bindings>
<services>
<service name="Backend.DemoService" behaviorConfiguration="ServiceBehaviour">
<endpoint binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
代理
<system.serviceModel>
<bindings>
<customBinding>
<binding name="rest-https-nokeepalive">
<webMessageEncoding />
<httpsTransport manualAddressing="true" allowCookies="false" keepAliveEnabled="false" maxBufferSize="10000000" maxReceivedMessageSize="10000000" maxBufferPoolSize="10000000" />
</binding>
</customBinding>
</bindings>
<services>
<service name="Proxy.DemoService" behaviorConfiguration="ServiceBehaviour">
<endpoint binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</service>
</services>
<client>
<endpoint name="Backend" address="https://wcfproxydemo.azurewebsites.net/DemoService.svc" binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</client>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000" />
</diagnostics>
</system.serviceModel>
测试前端
<system.serviceModel>
<bindings>
<customBinding>
<binding name="rest-https-nokeepalive">
<webMessageEncoding />
<httpsTransport manualAddressing="true" allowCookies="false" keepAliveEnabled="false" maxBufferSize="10000000" maxReceivedMessageSize="10000000" maxBufferPoolSize="10000000" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint name="Direct" address="https://wcfproxydemo.azurewebsites.net/DemoService.svc" binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
<endpoint name="Proxied" address="https://localhost:44388/DemoService.svc" binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000"/>
</diagnostics>
</system.serviceModel>
从前端到后端的直接连接工作正常。代理连接失败,因为后端不满意它从代理收到的请求,原因很明显。
我下载了你的代码并重现了你的问题,第二次请求报400 bad request错误。
我re-construct你的项目用客户端代理class调用代理项目和Tester项目中的服务,当我去掉代理项目中的interface属性时(我用的是三个接口,虽然有相同的),有效。
[WebInvoke(Method = "POST")]
ConsoleApp1.ServiceReference1.DemoResponse DoTheThing(ConsoleApp1.ServiceReference1.DemoRequest request);
此外,我们可以在Proxy项目中使用WebOperationContext class更改第二个传出请求的内容类型,请参考以下代码片段。
public DemoResponse DoTheThing(DemoRequest request)
{
request.Bar += "-Proxied";
IDemoService service = factory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope((IContextChannel)service))
{
WebOperationContext woc = WebOperationContext.Current;
woc.OutgoingRequest.ContentType = "application/json; charset=utf-8";
var result = service.DoTheThing(request);
try
{
return result;
}
finally
{
if (result is System.ServiceModel.ICommunicationObject client)
{
if (client.State == System.ServiceModel.CommunicationState.Faulted) client.Abort();
else client.Close();
}
}
}
}
如果有什么我可以帮忙的,请随时告诉我。
总结
我在 Azure 应用程序服务中有一个 WCF 后端服务 运行,我正在开发一个代理,它将使用相同的接口,但会处理一些消息以与客户端系统兼容。我知道这有点含糊,但细节并不重要。
问题是,当来自 Visual Studio 17 的 IIS Express 中的代理 运行 调用后端时,它这样做是不正确的,并且后端拒绝了请求。 (奇怪的是,拒绝的确切性质在真实服务和我为此 post 创建的最小可重现测试用例之间有所不同,但差异已经足够晚了,我认为它不相关)。
通过向 System.Net
添加跟踪侦听器,我可以观察到代理对后端的调用在 JSON 中有一个 body,但是 header 声称它是 XML:
System.Net Verbose: 0 : [4140] Data from ConnectStream#29742526::Write
System.Net Verbose: 0 : [4140] 00000000 : 7B 22 42 61 72 22 3A 22-42 2D 50 72 6F 78 69 65 : {"Bar":"B-Proxie
System.Net Verbose: 0 : [4140] 00000010 : 64 22 2C 22 46 6F 6F 22-3A 22 41 22 7D : d","Foo":"A"}
System.Net Verbose: 0 : [4140] Exiting ConnectStream#29742526::Write()
System.Net Verbose: 0 : [4140] Entering ConnectStream#29742526::Close()
System.Net Verbose: 0 : [4140] Exiting ConnectStream#29742526::Close()
System.Net Verbose: 0 : [4140] Entering HttpWebRequest#45658036::GetResponse()
System.Net Information: 0 : [4140] HttpWebRequest#45658036 - Request: POST /DemoService.svc/DoTheThing HTTP/1.1
System.Net Information: 0 : [4140] ConnectStream#29742526 - Sending headers
{
Content-Type: application/xml; charset=utf-8
Host: wcfproxydemo.azurewebsites.net
Content-Length: 29
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Close
}.
我可以更改一些配置以使其发送 JSON 和正确的 Content-Type header 吗? 我不想这样做必须将所有内容切换到 XML.
详情
我的最小可重现测试用例是 available on Github。我唯一省略的是将后端推送到 Azure 的发布配置文件。
各个项目共享一个接口项目,没有auto-generated服务引用。 (这可能是一种可能的前进方式,但似乎 heavy-weight,所以我更喜欢 config-based 解决方案)。三个组件的 WCF 配置是
后端
<system.serviceModel>
<bindings>
<customBinding>
<binding name="rest-https-nokeepalive">
<webMessageEncoding />
<httpsTransport manualAddressing="true" allowCookies="false" keepAliveEnabled="false" maxBufferSize="10000000" maxReceivedMessageSize="10000000" maxBufferPoolSize="10000000" />
</binding>
</customBinding>
</bindings>
<services>
<service name="Backend.DemoService" behaviorConfiguration="ServiceBehaviour">
<endpoint binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
代理
<system.serviceModel>
<bindings>
<customBinding>
<binding name="rest-https-nokeepalive">
<webMessageEncoding />
<httpsTransport manualAddressing="true" allowCookies="false" keepAliveEnabled="false" maxBufferSize="10000000" maxReceivedMessageSize="10000000" maxBufferPoolSize="10000000" />
</binding>
</customBinding>
</bindings>
<services>
<service name="Proxy.DemoService" behaviorConfiguration="ServiceBehaviour">
<endpoint binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</service>
</services>
<client>
<endpoint name="Backend" address="https://wcfproxydemo.azurewebsites.net/DemoService.svc" binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</client>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000" />
</diagnostics>
</system.serviceModel>
测试前端
<system.serviceModel>
<bindings>
<customBinding>
<binding name="rest-https-nokeepalive">
<webMessageEncoding />
<httpsTransport manualAddressing="true" allowCookies="false" keepAliveEnabled="false" maxBufferSize="10000000" maxReceivedMessageSize="10000000" maxBufferPoolSize="10000000" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint name="Direct" address="https://wcfproxydemo.azurewebsites.net/DemoService.svc" binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
<endpoint name="Proxied" address="https://localhost:44388/DemoService.svc" binding="customBinding" bindingConfiguration="rest-https-nokeepalive" contract="Interface.IDemoService" behaviorConfiguration="web" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000"/>
</diagnostics>
</system.serviceModel>
从前端到后端的直接连接工作正常。代理连接失败,因为后端不满意它从代理收到的请求,原因很明显。
我下载了你的代码并重现了你的问题,第二次请求报400 bad request错误。 我re-construct你的项目用客户端代理class调用代理项目和Tester项目中的服务,当我去掉代理项目中的interface属性时(我用的是三个接口,虽然有相同的),有效。
[WebInvoke(Method = "POST")]
ConsoleApp1.ServiceReference1.DemoResponse DoTheThing(ConsoleApp1.ServiceReference1.DemoRequest request);
此外,我们可以在Proxy项目中使用WebOperationContext class更改第二个传出请求的内容类型,请参考以下代码片段。
public DemoResponse DoTheThing(DemoRequest request)
{
request.Bar += "-Proxied";
IDemoService service = factory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope((IContextChannel)service))
{
WebOperationContext woc = WebOperationContext.Current;
woc.OutgoingRequest.ContentType = "application/json; charset=utf-8";
var result = service.DoTheThing(request);
try
{
return result;
}
finally
{
if (result is System.ServiceModel.ICommunicationObject client)
{
if (client.State == System.ServiceModel.CommunicationState.Faulted) client.Abort();
else client.Close();
}
}
}
}
如果有什么我可以帮忙的,请随时告诉我。