如何使用 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();
    }
}