Как я могу использовать строго типизированные параметры в пути URI в WCF с WebHttpBinding
Хорошо, я искал кучу ответов на этот вопрос, и мне кажется, что я могу только найти статьи и документы для.NET 3.5 с 2011 года и обратно... поэтому я надеюсь найти более свежую информацию для WCF в.NET 4.5 (и вверх).
У меня есть служба WCF, которая связана с webHttpBinding. Он использует шаблон Uri, который передает идентификатор элемента, который должен быть извлечен с помощью шаблона Uri, следующим образом:
[WebInvoke(Method="GET", UriTemplate = "/{identity}")]
public ResponseItem Get(string identity)
{
//Convert identity to a Guid which is the correct type for the identity
//Retrieve object
}
Что я действительно хотел бы сделать, это удалить преобразование значения в Guid и переместить его вверх по стеку, чтобы очистить код и получить:
[WebInvoke(Method="GET", UriTemplate = "/{identity}")]
public ResponseItem Get(Guid identity)
{
//Retrieve object
}
Я понимаю, что с другими типами привязки это возможно, используя пользовательское поведение и QueryStringConverter. Я также понимаю, что причина того, что это по умолчанию строка в webHttpBinding, заключается в том, что значения, передаваемые в адресе, должны быть семантически семантическими, поскольку адрес основан на строках. Так что, возможно, то, что я спрашиваю, может не иметь смысла.
В контексте моего приложения строка не является семантически правильной, и меня раздражает, что этот класс загроможден кодом преобразования, который не должен беспокоить класс, но изменение привязки не вариант, так как его используют существующие клиенты.
Существует ли точка расширения в конвейере WCF для текущих версий WCF (например, IParameterInspector, IServiceBehavior), где преобразование этого значения возможно и целесообразно, чтобы к моменту вызова метода параметр мог иметь правильный тип?
2 ответа
Обновленный ответ -
Посмотрев на ваш комментарий, я понял. Таким образом, вы хотите предоставить строку и затем привести ее до того, как появится OperationInovker. Так что я испачкал свои руки, играя и наконец сделал это.
Вот что я сделал. Новый класс, производный от WebHttpBehavior
который дал бы место, где мы можем расширить или переопределить существующее поведение WebHttpBidning
,
public class MyWebHttpBehavior : WebHttpBehavior
{
}
Хотя это взломать, но это будет работать. Проблема с аргументом Typed заключалась в том, что средство форматирования шаблона URL выдавало исключение, проверяя сигнатуру метода для типа string
так что я избавился от этого, покатавшись на BindingInformation
методом оверидинга GetRequestDispatchFormatter
,
protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
foreach (var item in operationDescription.Messages[0].Body.Parts)
{
item.Type = typeof(string);
}
return base.GetRequestDispatchFormatter(operationDescription, endpoint);
}
После применения этого поведения время выполнения больше не будет выдавать исключение для проверки аргумента String. Теперь мне нужно изменить OperationInvoker, потому что если вы запустите это, то это вызовет исключение, когда вы вызовете опрацию из клиента, говоря Invalid cast
,
Теперь вот идет IOperationInvoker
на картине. Я просто взял значение из input[] объекта типа, преобразовал значение из String
в Guid
и передал его обратно Invoker.
public class ValueCastInvoker : IOperationInvoker
{
readonly IOperationInvoker _invoker;
public ValueCastInvoker(IOperationInvoker invoker)
{
_invoker = invoker;
}
public ValueCastInvoker(IOperationInvoker invoker, Type type, Object value)
{
_invoker = invoker;
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs().ToArray();
}
private object[] CastCorrections(object[] inputs)
{
Guid obj;
var value = inputs[0] as string;
if (Guid.TryParse(value, out obj))
{
return new[] { (object)obj }.Concat(inputs.Skip(1)).ToArray();
}
return inputs.ToArray();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
return _invoker.Invoke(instance, CastCorrections(inputs), out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _invoker.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _invoker.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return _invoker.IsSynchronous; }
}
}
Теперь вот что мне потребовалось много времени, чтобы понять, как внедрить эту пользовательскую операцию inovker в конвейер. Я нашел здесь ответ на stackru. И реализовал так, как предложил человек, и это сработало.
Добавление резюме здесь: должен быть реализован новый IOperationBehavior и присоединить его с помощью DispatcherRuntime.
public class MyOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new ValueCastInvoker(dispatchOperation.Invoker);
}
public void Validate(OperationDescription operationDescription)
{
}
}
Теперь в MyWebHttpBehavior переопределяем ApplyDispatchBehavior
и представить выше реализованное IOperationBehavior
,
public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (var operation in endpoint.Contract.Operations)
{
if (operation.Behaviors.Contains(typeof(MyOperationBehavior)))
continue;
operation.Behaviors.Add(new MyOperationBehavior());
}
base.ApplyDispatchBehavior(endpoint, endpointDispatcher);
}
Теперь все эти хаки и расширения сделали бы это законным.
[WebInvoke(Method = "GET", UriTemplate = "/{id}")]
string GetValue(Guid id);
Отказ от ответственности: мне было весело экспериментировать с этим расширением и применять нестандартное поведение, но я не проверял затронутые области. Так что используйте его на свой страх и риск и не стесняйтесь изменять / улучшать, как вы можете. Извините за опечатки.
Обновление 2
Я создал библиотеку, чтобы обернуть это расширение поведения веб-http. Библиотека обеспечивает большую поддержку других типов значений в параметрах метода (несколько). Проверьте это.
Вам необходимо предоставить пользовательскую реализацию QueryStringConverter. Вот код, который вам нужен. Просто добавьте GuidConverterWebHttpBehavior как поведение службы, и все должно работать.
class GuidQueryStringConverter : QueryStringConverter
{
public override bool CanConvert(Type type)
{
return type == typeof(Guid) || base.CanConvert(type);
}
public override object ConvertStringToValue(string parameter, Type parameterType)
{
if (parameterType == typeof(Guid))
{
Guid guid;
if(Guid.TryParse(parameter, out guid))
{
return guid;
}
}
return base.ConvertStringToValue(parameter, parameterType);
}
}
public class GuidConverterWebHttpBehavior : WebHttpBehavior
{
protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
{
return new GuidQueryStringConverter();
}
}