Динамическое назначение типа содержимого ответа json в служебной шине WCF Azure

Я пишу приложение POC, которое использует Microsoft.ServiceBus.dll 1.0.0.0 (версия .NET 3.5).

Мой контракт и сервис WCF выглядят следующим образом:

[ServiceContract(Name="MyServiceContract", Namespace = "http://mydomain.com/")]
internal interface IServiceContract
{
    [WebInvoke(Method = "POST", UriTemplate = "/DoOperation")]
    [OperationContract]
    Stream RelayRequest(Stream requestBody);
}

[ServiceBehavior(Name = "Service1", Namespace = "http://mydomain.com/Service1/", InstanceContextMode = InstanceContextMode.Single)]
internal class Service1 : IServiceContract
{
    Stream RelayRequest(Stream requestBody)
    {
        var contents = GetJsonResponse();
        var responseStream = new MemoryStream();
        var streamWriter = new StreamWriter(responseStream);
        streamWriter.AutoFlush = true;
        var writer = new JsonTextWriter(streamWriter);
        var serializer = new JsonSerializer();
        serializer.Serialize(responseStream, contents);

        responseStream.Position = 0 // reset the position of the stream so that it's contents will be read from the beginning.

        //Problem Line:
        WebOperationContext.OutgoingResponse.ContentType = "application/json";
        return responseStream;
    }
}

Конечная точка прослушивания настроена на использование WebHttpRelayBinding:

  • SecurityMode: Транспорт
  • Режим передачи: потоковый

Когда я пытаюсь присвоить ContentType исходящего ответа «application/json», ошибка не возникает, но запрос вызова возвращается с кодом состояния 504 (время ожидания шлюза).

Если я изменю ContentType на «text/javascript», запрос на вызов вернется с кодом 200 (ОК).

Некоторые вещи, которые следует отметить:

  • Тип содержимого неизвестен до времени выполнения, поэтому он должен назначаться динамически.
  • Содержимое потока чистое — 100% достоверное — json.
  • The intention for accepting and returning a stream is so we can accept streamed requests and stream data down to the client.
    • Each request / response could contain a small json payload or a 200MB document.
  • Если вы хотите воспроизвести — этот код использует библиотеку Newtonsoft Json для сериализации.

Почему это происходит и как я могу это исправить?

EDIT: код состояния 504 может быть отвлекающим маневром, сделанным скрипачом, с которым я тестирую. Отправка того же запроса из System.Net.Http.HttpClient указывает на то, что соединение закрывается до получения ответа.

РЕДАКТИРОВАТЬ: установка типа содержимого практически на что угодно (включая бессмысленные значения) работает нормально. Единственный тип содержимого, который я могу сломать, — это application/json.


person Gavin Osborn    schedule 13.02.2013    source источник
comment
Можете ли вы добавить информацию из вашего файла конфигурации / как вы настроили службу? Какой тип привязки вы используете?   -  person TheDude    schedule 20.02.2013
comment
Как указано выше, мы используем WebHttpRelayBinding. Эта привязка/конечная точка не создается конфигурацией, она создается программно во время выполнения.   -  person Gavin Osborn    schedule 20.02.2013
comment
Ах, думаю, у меня случился ряд психических срывов, когда я читал ваше описание...   -  person TheDude    schedule 20.02.2013
comment
Есть прогресс в решении этой проблемы?   -  person user381624    schedule 13.03.2013
comment
К сожалению нет, нет. В итоге мы обошли это с помощью настраиваемых заголовков.   -  person Gavin Osborn    schedule 13.03.2013
comment
[звук ворона] меня. это абсурд. Я думаю, я мог бы увидеть, что Годо предлагает мне сделать.   -  person user381624    schedule 13.03.2013


Ответы (1)


Вот как я заставил это работать, но это простая служба WCF, размещенная локально в IIS. Я не использую Microsoft.ServiceBus или Azure.

Ответ первого скрипача. Если это то, что вы ищете, переходите к коду :)

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 121
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 04 Apr 2013 17:17:05 GMT

[{"BoolValue":true,"StringValue":"blah"},{"BoolValue":false,"StringValue":"boo"},{"BoolValue":true,"StringValue":"floo"}]

Определение услуги:

namespace yourNS
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        [WebGet(UriTemplate = "/CT", ResponseFormat = WebMessageFormat.Json)]
        List<CompositeType> GetData();
    }


    [DataContract]
    public class CompositeType
    {
        [DataMember]
        public bool BoolValue { get; set; }

        [DataMember]
        public string StringValue { get; set; }
    }

    public class Service1 : IService1
    {
        public List<CompositeType> GetData()
        {
            return new List<CompositeType>
            {
                new CompositeType { BoolValue = true, StringValue = "blah" },
                new CompositeType { BoolValue = false, StringValue = "boo" },
                new CompositeType { BoolValue = true, StringValue = "floo" },
            };
        }
    }
}

И web.config

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="yourNS.Service1">
        <endpoint 
          address="" 
          binding="webHttpBinding" 
          behaviorConfiguration="MyBehave"
          contract="yourNS.IService1" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="MyBehave">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>
person Schultz9999    schedule 04.04.2013
comment
Ваше решение работает только потому, что вы убрали всю сложность исходного вопроса, который в первую очередь вызывал проблему. Как указано в исходном вопросе, мне нужно динамически назначать тип контента ответа, для этого мне также нужно использовать служебную шину Azure. - person Gavin Osborn; 05.04.2013