Skip to main content
 首页 » 编程设计

c#之如何使用 WebHttpBinding 在 WCF 的 uri 路径中使用强类型参数

2024年02月05日25dflying

好吧,我已经搜索了很多关于这个问题的答案,但我似乎只能找到 2011 年及以后的 .NET 3.5 的文章和文档......所以我希望能找到更多关于 WCF 的最新信息.NET 4.5(及更高版本)。

我有一个绑定(bind)到 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 
} 

我意识到对于其他类型的绑定(bind),这是可以使用自定义行为和 QueryStringConverter。我还意识到,在 webHttpBinding 中默认为字符串的原因是地址中传递的固有值应该在语义上是字符串——因为地址是基于字符串的。所以也许我问的可能没有意义。

在我的应用程序的上下文中,字符串在语义上是不正确的,这让我很恼火,因为这个类充满了转换代码,这不应该是这个类的关注点,但是改变绑定(bind)不是一个选项,因为现有的客户是使用它。

当前版本的 WCF(例如 IParameterInspector、IServiceBehavior)的 WCF 管道中是否有一个扩展点,其中此值的转换既可能又合适,以便在该方法是调用,参数可以是正确的类型?

请您参考如下方法:

Updated Answer -

看了你的评论,我明白了。因此,您想提供字符串,然后在 OperationInovker 出现之前对其进行转换。所以我弄脏了手,终于做到了。

这是我所做的。一个派生自 WebHttpBehavior 的新类,它将提供一个地方,我们可以在其中扩展或覆盖 WebHttpBidning 的现有行为。

public class MyWebHttpBehavior : WebHttpBehavior 
{ 
} 

虽然它是一个hack 但它会起作用。 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); 
    } 

应用此行为后,运行时将不再为字符串参数检查抛出异常。现在我需要更改 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; } 
    } 
} 

现在我花了很长时间才弄清楚如何在管道中注入(inject)这个自定义操作 inovker。我找到了相关的 stackoverflow 答案 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); 

免责声明:我很高兴尝试此扩展并应用自定义行为,但我没有检查受影响的区域。因此,使用它需要您自担风险,并可以随意更改/增强。 抱歉打字错误。

Update 2

我已经创建了一个库来包装这个 web http 行为扩展。该库在方法参数(多个)中提供了对其他值类型的更多支持。 Check this out .