.net - 为什么我的 WCF 代理发送内容类型为 : application/xml? 的 JSON

标签 .net wcf proxy wcf-binding

摘要

我有一个在 Azure 应用服务中运行的 WCF 后端服务,并且我正在开发一个代理,该代理将使用相同的接口(interface),但操作一些消息以与客户端系统兼容。我知道这有点含糊,但细节并不重要。

问题在于,当从 Visual Studio 17 在 IIS Express 中运行的代理调用后端时,它的做法不正确,并且后端拒绝了该请求。 (奇怪的是,实际服务和我为这篇文章创建的最小可重现测试用例之间的拒绝的确切性质有所不同,但这种差异已经太晚了,我不认为它是相关的)。

通过向 System.Net 添加跟踪监听器,我可以观察到代理对后端进行的调用具有 JSON 格式的正文,但 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
}.

我可以更改一些配置以使其发送具有正确 Content-Type header 的 JSON 吗?我不想将所有内容都切换为 XML。

详细信息

我的最小可重现测试用例是 available on Github 。我唯一省略的是将后端推送到 Azure 的发布配置文件。

各个项目共享一个接口(interface)项目,并且没有自动生成的服务引用。 (这可能是一种可能的前进方式,但它看起来相当重量级,所以我更喜欢基于配置的解决方案)。这三个组件的 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 错误。 我重新构建你的项目并使用客户端代理类来调用代理项目和Tester项目中的服务,当我删除代理项目中的接口(interface)属性时(我使用了三个接口(interface),尽管它们是相同的),它有效。

[WebInvoke(Method = "POST")]
ConsoleApp1.ServiceReference1.DemoResponse DoTheThing(ConsoleApp1.ServiceReference1.DemoRequest request);

此外,我们可以通过使用Proxy项目中的WebOperationContext类来更改第二个传出请求的内容类型,请引用下面的代码片段。

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();
                }
            }
        }
    }

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

关于.net - 为什么我的 WCF 代理发送内容类型为 : application/xml? 的 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56703208/

相关文章:

c# - 如何创建自时间触发功能?

c# - T 不包含 RowKey 的定义

.net - 信号量计数

jquery - 使用 json 向 RESTful WCF 发送 Post 请求

reactjs - 服务器重定向不适用于 ReactJS 代理服务器

ruby-on-rails - Apache 反向代理 Unix 套接字

c# - SQLite .NET 性能,如何加快速度?

.net - 如何在 WCF 中自动重新连接命名管道绑定(bind)

c# - WCF 异常--故障状态

node.js - NgInx 静态内容 Nodejs 应用程序反向代理禁止错误