为什么我的 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();
                }
            }
        }
    }

如果有什么我可以帮忙的,请随时告诉我。