Как в модели веб-программирования WCF написать контракт операции с массивом параметров строки запроса (то есть с тем же именем)?

Используя модель веб-программирования WCF, можно указать такой контракт операции:

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "SomeRequest?qs1={qs1}&qs2={qs2}")]
XElement SomeRequest1(string qs1, string qs2);

Теперь, если бы нам нужно было заключить контракт, который принимает массив параметров с тем же именем (в данном случае qs1), контракт вот так ...

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "SomeRequest?qs1={qs1}&qs1={qs2}")]
 XElement SomeRequest2(string qs1, string qs2);

Мы получаем сообщение об ошибке во время выполнения, когда вызываем метод:

строка запроса должна содержать пары «имя = значение» с уникальными именами. Обратите внимание, что в именах регистр не учитывается. См. Документацию для UriTemplate для получения более подробной информации.

Как определить службу HTTP, которая предоставляет ресурс с массивом параметров, не прибегая к бесполезному интерфейсу?


person Dilip Krishnan    schedule 10.12.2008    source источник
comment
Если вам не нужен [WebGet], вы можете вместо него использовать [WebInvoke(Method = "POST"], в котором разрешены массивы / коллекции. См. этот ответ.   -  person stomy    schedule 12.04.2018


Ответы (3)


Я реализовал простой пользовательский QueryStringConverter, чтобы вы могли сделать qs1 строкой [], а затем разделить переменную строки запроса запятыми (например, http://server/service/SomeRequest?Qs1=val1,val2,val3,val4)

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml,
        UriTemplate = "SomeRequest?qs1={qs1}")]
XElement SomeRequest2(string[] qs1);

Сначала вам нужен класс, который наследуется от WebHttpBehavior, чтобы мы могли внедрить наш собственный QueryStringConverter:

public class CustomHttpBehavior : System.ServiceModel.Description.WebHttpBehavior
{
    protected override System.ServiceModel.Dispatcher.QueryStringConverter GetQueryStringConverter(System.ServiceModel.Description.OperationDescription operationDescription)
    {
        return new CustomQueryStringConverter();
    }
}

Затем наш CustomQueryStringConverter, который обрабатывает параметры string []:

public class CustomQueryStringConverter : System.ServiceModel.Dispatcher.QueryStringConverter
{
    public override bool CanConvert(Type type)
    {
        if (type == typeof(string[]))
        {
            return true;
        }

        return base.CanConvert(type);
    }

    public override object ConvertStringToValue(string parameter, Type parameterType)
    {
        if (parameterType == typeof(string[]))
        {
            string[] parms = parameter.Split(',');
            return parms;
        }

        return base.ConvertStringToValue(parameter, parameterType);
    }

    public override string ConvertValueToString(object parameter, Type parameterType)
    {
        if (parameterType == typeof(string[]))
        {
            string valstring = string.Join(",", parameter as string[]);
            return valstring;
        }

        return base.ConvertValueToString(parameter, parameterType);
    }
}

Последнее, что вам нужно сделать, это создать расширение конфигурации поведения, чтобы среда выполнения могла получить экземпляр CustomWebHttpBehavior:

public class CustomHttpBehaviorExtensionElement : System.ServiceModel.Configuration.BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new CustomHttpBehavior();
    }

    public override Type BehaviorType
    {
        get { return typeof(CustomHttpBehavior); }
    }
}

Теперь мы добавляем элемент в наши расширения конфигурации, чтобы использовать наш CustomWebHttpBehavior, мы используем имя этого расширения вместо <webHttp /> в нашем поведении:

 <system.serviceModel>
   <services>
     <service name="NameSpace.ServiceClass">
       <endpoint address="" behaviorConfiguration="MyServiceBehavior"
        binding="webHttpBinding" contract="NameSpace.ServiceClass" />
     </service>
   </services>
  <behaviors>
   <endpointBehaviors>
    <behavior name="MyServiceBehavior">
      <customWebHttp/>
    </behavior>
   </endpointBehaviors>
  </behaviors>
  <extensions>
    <behaviorExtensions>
      <add name="customWebHttp" type="NameSpace.CustomHttpBehaviorExtensionElement, MyAssemblyName" />
    </behaviorExtensions>
  </extensions>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
 </system.serviceModel>

Теперь вы также можете расширить свой CustomQueryStringConverter для обработки других типов, которые не используются по умолчанию, например типов значений, допускающих значение NULL.

ПРИМЕЧАНИЕ. В Microsoft Connect регистрируется ошибка, которая напрямую связана с этим кодом. Код фактически не работает почти во всех случаях, когда вы пытаетесь преобразовать запросы различных типов.

http://connect.microsoft.com/VisualStudio/feedback/details/616486/bug-with-getquerystringconverter-not-being-called-by-webservicehost#tabs

Убедитесь, что вы внимательно прочитали это, прежде чем тратить часы своего времени на создание настраиваемых преобразователей строк запроса REST, которые не могут работать. (Относится к Framework 4.0 и ниже).

person joshperry    schedule 10.12.2008
comment
В WCF 4 есть ошибка, из-за которой WebServiceHost не вызывает переопределенный метод GetQueryStringConverter. См. connect.microsoft.com/VisualStudio/feedback/details/616486/ - person Daniel; 14.11.2010
comment
это действительно работает? Я не смог заставить это работать - person Daniel; 12.03.2011

Чтобы ответить на ваш комментарий к другому моему ответу:

Вы можете использовать подстановочный знак в конце строки запроса, например

[WebGet(ResponseFormat = WebMessageFormat.Xml,
        UriTemplate = "SomeRequest?qs1={*qs1}")]
XElement SomeRequest2(string qs1);

Таким образом, строковый параметр qs1 будет всей необработанной строкой запроса после qs1 =, вы можете затем проанализировать это вручную в своем коде.

QueryStringConverter полагается на форматирование строки запроса, поэтому сделать что-то именно так, как вы хотите, невозможно без возможной перезаписи QueryStringConverter вместо небольших переопределений, которые мы сделали в другом ответе.

Из MSDN:

Сегменты с подстановочными знаками должны соответствовать следующим правилам:

  • Для каждой строки шаблона может быть не более одного именованного сегмента подстановочного знака.
  • Именованный сегмент с подстановочным знаком должен находиться в самом правом сегменте пути.
  • Именованный сегмент подстановочного знака не может сосуществовать с анонимным сегментом подстановочного знака в одной строке шаблона.
  • Имя именованного сегмента с подстановочными знаками должно быть уникальным.
  • Именованные сегменты с подстановочными знаками не могут иметь значений по умолчанию.
  • Именованные сегменты с подстановочными знаками не могут заканчиваться на «/».
person joshperry    schedule 10.12.2008

Имейте в виду, что в WCF 3.5 вы должны указать полное имя сборки в:

   <extensions>
    <behaviorExtensions>
      <add name="customWebHttp" type="NameSpace.CustomHttpBehaviorExtensionElement, MyAssemblyName, NOT SUFFICIENT HERE" />
    </behaviorExtensions>
  </extensions>

Вот так: SampleService.CustomBehavior, SampleService, Версия = 1.0.0.0, Культура = нейтральный, PublicKeyToken = null

В противном случае вы получите исключение:

Ошибка конфигурации
Описание: произошла ошибка во время обработки файла конфигурации, необходимого для обслуживания этого запроса. Просмотрите приведенные ниже сведения о конкретных ошибках и соответствующим образом измените файл конфигурации.

Сообщение об ошибке парсера: недопустимый элемент в конфигурации. Имя расширения CustomWebHttp не зарегистрировано в коллекции в system.serviceModel / extensions / behaviorExtensions.

person pootow    schedule 24.12.2009