如何使用 WebHttpBinding 在 WCF 的 uri 路径中使用强类型参数
How can I use strongly typed parameters in the uri path in WCF with WebHttpBinding
好的,我已经搜索了很多关于这个问题的答案,但我似乎只能找到 2011 年及以后的 .NET 3.5 文章和文档...所以我希望找到更多最新信息.NET 4.5(及更高版本)中的 WCF。
我有一个绑定到 webHttpBinding 的 WCF 服务。它使用 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 中默认为字符串的原因是地址 中传递的固有值在语义上应该是字符串 - 因为地址是基于字符串的。所以也许我问的可能没有意义。
在我的应用程序的上下文中,字符串在语义上是不正确的,这让我很恼火,这个 class 被转换代码弄得乱七八糟,这不应该是 class 关心的问题,但是改变绑定不是一个选项,因为现有客户正在使用它。
当前版本的 WCF(例如 IParameterInspector、IServiceBehavior)的 WCF 管道中是否有一个扩展点,其中此值的转换都是可能的 和 适当的,以便通过调用方法时,参数类型是否正确?
Updated Answer -
看了你的评论我明白了。因此,您想提供字符串,然后在 OperationInovker 出现之前对其进行转换。所以我弄脏了手,终于做到了。
这是我所做的。派生自 WebHttpBehavior
的新 class 将提供一个地方,我们可以在其中扩展或覆盖 WebHttpBidning
.
的现有行为
public class MyWebHttpBehavior : WebHttpBehavior
{
}
尽管这是一个 hack,但这会奏效。 Typed 参数的问题是 URL 模板格式化程序通过检查类型 string
的方法签名抛出异常,所以我通过覆盖方法 [=] 覆盖 BindingInformation
来摆脱它20=].
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);
}
应用此行为后,运行 时间将不再抛出字符串参数检查异常。现在我需要更改 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。我找到了相关的 Whosebug 答案 here。并按照人们建议的方式实施并奏效。
在此处添加摘要: 必须实施新的 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);
}
现在所有这些 hack 和扩展都会使它合法。
[WebInvoke(Method = "GET", UriTemplate = "/{id}")]
string GetValue(Guid id);
免责声明: 我很高兴尝试此扩展并应用自定义行为,但我没有检查受影响的区域。因此,使用它需要您自担风险,请随意使用 change/enhance。 抱歉错别字。
Update 2
我已经创建了一个库来包装这个 web http 行为扩展。该库在方法参数(多个)中提供了对其他值类型的更多支持。 Check this out。
您需要提供 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();
}
}
好的,我已经搜索了很多关于这个问题的答案,但我似乎只能找到 2011 年及以后的 .NET 3.5 文章和文档...所以我希望找到更多最新信息.NET 4.5(及更高版本)中的 WCF。
我有一个绑定到 webHttpBinding 的 WCF 服务。它使用 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 中默认为字符串的原因是地址 中传递的固有值在语义上应该是字符串 - 因为地址是基于字符串的。所以也许我问的可能没有意义。
在我的应用程序的上下文中,字符串在语义上是不正确的,这让我很恼火,这个 class 被转换代码弄得乱七八糟,这不应该是 class 关心的问题,但是改变绑定不是一个选项,因为现有客户正在使用它。
当前版本的 WCF(例如 IParameterInspector、IServiceBehavior)的 WCF 管道中是否有一个扩展点,其中此值的转换都是可能的 和 适当的,以便通过调用方法时,参数类型是否正确?
Updated Answer -
看了你的评论我明白了。因此,您想提供字符串,然后在 OperationInovker 出现之前对其进行转换。所以我弄脏了手,终于做到了。
这是我所做的。派生自 WebHttpBehavior
的新 class 将提供一个地方,我们可以在其中扩展或覆盖 WebHttpBidning
.
public class MyWebHttpBehavior : WebHttpBehavior
{
}
尽管这是一个 hack,但这会奏效。 Typed 参数的问题是 URL 模板格式化程序通过检查类型 string
的方法签名抛出异常,所以我通过覆盖方法 [=] 覆盖 BindingInformation
来摆脱它20=].
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);
}
应用此行为后,运行 时间将不再抛出字符串参数检查异常。现在我需要更改 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。我找到了相关的 Whosebug 答案 here。并按照人们建议的方式实施并奏效。
在此处添加摘要: 必须实施新的 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);
}
现在所有这些 hack 和扩展都会使它合法。
[WebInvoke(Method = "GET", UriTemplate = "/{id}")]
string GetValue(Guid id);
免责声明: 我很高兴尝试此扩展并应用自定义行为,但我没有检查受影响的区域。因此,使用它需要您自担风险,请随意使用 change/enhance。 抱歉错别字。
Update 2
我已经创建了一个库来包装这个 web http 行为扩展。该库在方法参数(多个)中提供了对其他值类型的更多支持。 Check this out。
您需要提供 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();
}
}