Почему мой прокси-сервер WCF отправляет JSON с Content-Type: application / xml? - PullRequest
2 голосов
/ 21 июня 2019

Сводка

У меня есть внутренняя служба WCF, работающая в службе приложений Azure, и я разрабатываю прокси-сервер, который будет использовать тот же интерфейс, но манипулировать некоторыми сообщениями для совместимости с системой клиента.Я знаю, что это немного расплывчато, но детали не важны.

Проблема в том, что когда прокси-сервер, работающий в IIS Express из Visual Studio 17, вызывает бэкэнд, он делает это неправильно, иБэкэнд отклоняет запрос.(Любопытно, что точный характер отклонения отличается между реальным сервисом и минимальным воспроизводимым контрольным примером, который я создал для этого поста, но разница достаточно поздняя, ​​и я не считаю, что это актуально).

Добавив прослушиватели трассировки в System.Net, я могу заметить, что вызов, сделанный прокси-сервером для внутреннего интерфейса, имеет тело в JSON, но заголовки, утверждающие, что это 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? Я бы предпочел не переключать все на XML.

Подробности

Мой минимальный воспроизводимый тестчехол доступен на Github .Единственное, что я выбрал, - это профиль публикации, чтобы перенести бэкэнд в Azure.

Различные проекты имеют общий интерфейс, и нет автоматически сгенерированной ссылки на службу.(Это может быть возможным способом продвижения вперед, но он кажется довольно тяжелым, поэтому я бы предпочел решение на основе конфигурации).Конфигурация WCF для трех компонентов:

Backend

  <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>

Proxy

  <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>

Прямое подключение отинтерфейс к бэкэнду работает нормально.Прокси-соединение не удается, потому что сервер не удовлетворен запросом, который он получает от прокси-сервера, по очевидным причинам.

1 Ответ

2 голосов
/ 25 июня 2019

Я скачал ваш код и воспроизвел вашу проблему, второй запрос сообщает об ошибке 400 неверных запросов.Я реконструирую ваш проект и использую прокси-класс клиента для вызова сервиса в прокси-проекте и проекте Tester, и когда я удаляю атрибут интерфейса в прокси-проекте (я использую три интерфейса, хотя они идентичны), онработает.

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

Кроме того, мы можем изменить тип содержимого второго исходящего запроса, используя класс WebOperationContext в проекте Proxy, см. фрагменты кода ниже.

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

Не стесняйтесьдайте мне знать, если есть что-то, с чем я могу помочь.

...