Я пытаюсь разрешить гибкие параметры метода внутри моей службы WCF, но столкнулся с несколькими проблемами.
Сначала я просто объясню цель. Я хочу иметь возможность объявить веб-метод, например:
[OperationContract, WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)]
public StatisticEventArgs GetStatistic(DateTime? startDate = null, DateTime? endDate = null)
{
return new StatisticEventArgs() { Count = 50; }
}
и иметь возможность вызывать его через метод jquery $ .ajax:
$.ajax({
type: 'GET',
url: 'service/MyService.svc,
data: '', // or '?startDate=12/1/2011&endDate=12/10/2011','?startDate=12/1/2011', etc. Any combination of startDate or endDate parameters
contentType: "application/json",
dataType: "json",
processdata: true,
success: endCallback,
error: errorCallback
});
Цель состоит в том, чтобы создать методы, в которых все параметры являются необязательными, и для любых параметров, которые не указаны, вместо них будет использоваться их значение по умолчанию. В этом примере я хочу иметь возможность вызвать метод, в котором я могу предоставить параметр startDate или endDate, оба или ни один из них.
Я быстро обнаружил, что класс QueryStringParameter по умолчанию (как видно в этом вопросе: В модели веб-программирования WCF, как можно написать контракт операции с массивом параметров строки запроса (то есть с тем же именем)?) не поддерживает типы, допускающие значение NULL, но JsonQueryStringConverter поддерживает. Кроме того, как отмечено в приведенном выше вопросе, в .NET 4.0 и ниже имеется ошибка, связанная с этим решением, которая не позволяет создать экземпляр класса JsonQueryStringConverter в настраиваемом поведении (GetQueryStringConverter никогда не вызывается).
После дополнительных исследований я обнаружил эта ошибка Microsoft, которая предоставляет обходной путь, но только при создании экземпляра службы с помощью кода.
У меня вопрос, как я могу применить это обходное решение в веб-службе ASP.NET WCF? Я считаю, что мне нужно настроить его в web.config, но я не уверен, как это сделать . Вот соответствующий пример кода для того, что у меня есть (я изменил имена, поэтому он может содержать некоторые опечатки):
Класс WCF, содержащийся в моем проекте веб-приложения ASP.NET:
namespace Namespace.Services
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService
{
[OperationContract, WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)]
public StatisticEventArgs GetStatistic(DateTime? startDate = null, DateTime? endDate = null)
{
return new StatisticEventArgs() { Count = 50; }
}
public class NullableTypeBehaviorBehaviorExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(NullableTypeBehavior); }
}
protected override object CreateBehavior()
{
return new NullableTypeBehavior();
}
}
public class NullableTypeBehavior : WebHttpBehavior
{
protected override System.ServiceModel.Dispatcher.QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
{
return new JsonQueryStringConverter();
}
}
}
Javascript:
$.ajax({
type: 'GET',
url: 'service/MyService.svc/GetStatistic',
data: '', // or '?startDate=12/1/2011&endDate=12/10/2011','?startDate=12/1/2011', etc. Any combination of startDate or endDate parameters
contentType: "application/json",
dataType: "json",
processdata: true,
success: endCallback,
error: errorCallback
});
web.config:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="nullableTypeBehavior" type="Namespace.Services.NullableTypeBehaviorBehaviorExtension, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"></serviceHostingEnvironment>
<services>
<service name="MyWcfService">
<endpoint address="" behaviorConfiguration="MyBehaviorConfig"
binding="webHttpBinding" contract="Namespace.Services.MyService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="web">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyBehaviorConfig">
<nullableTypeBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
</client>
UPDATE 1: SOLUTION
Вот ответ, который я получил (любезно предоставлено VinayC):
public class CustomFactory : System.ServiceModel.Activation.ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type t, Uri[] baseAddresses)
{
return new CustomHost(t, baseAddresses);
}
}
public class CustomHost : ServiceHost
{
public CustomHost(Type t, params Uri[] baseAddresses) : base(t, baseAddresses) { }
protected override void OnOpening()
{
var nullableTypeBehavior = new NullableTypeBehavior();
foreach(var ep in this.Description.Endpoints)
{
ep.Behaviors.Add(nullableTypeBehavior);
}
base.OnOpening();
}
}
и в верхней части моего файла MyService.svc:
<%@ ServiceHost Language="C#" Debug="true" Service="Namespace.Services.MyService" CodeBehind="MyService.svc.cs" Factory="Namespace.Services.CustomFactory" %>
ОБНОВЛЕНИЕ 2: проблема с датами передачи этого решения
В настоящее время не работает передача дат:
http://localhost/Services/MyService.svc?startDate=11/10/2011&endDate=11/11/2011
но эти запросы работают:
http://localhost/Services/MyService.svc?startDate=null&endDate=null
http://localhost/Services/MyService.svc
Похоже, это специфичный для JsonQueryStringConverter, поэтому я собираюсь изучить его и опубликовать ответ, когда найду решение. Похоже, это должно быть достаточно просто для отладки.
ОБНОВЛЕНИЕ 3. Решение для переноса дат:
Проблема в том, что даты, которые я передавал, не соответствуют ожидаемому формату для .NET JSON. Я проходил что-то вроде:
"12/01/2011"
и .NET JSON (включая JsonQueryStringConverter) ожидает этого формата:
"\/Date(1283219926108)\/"
Этот формат даты не является родным для javascript. Чтобы получить дату в этом формате, я нашел этот пост. После добавления этого скрипта я могу сделать это в jQuery, и дата будет проанализирована соответствующим образом:
$.ajax({
type: 'GET',
url: 'service/MyService.svc/GetStatistic',
data: '?startDate=' + JSON.stringifyWCF(new Date('12/1/2011'))
contentType: "application/json",
dataType: "json",
processdata: true,
success: endCallback,
error: errorCallback
});
Вроде все работает как положено. Я могу игнорировать любые необязательные параметры, установить для них значение null или присвоить им значение, и все запросы будут работать.
Обновлена ошибка Microsoft с обходным решением. Обходной путь немного более элегантен, чем решение, представленное здесь.