如何在 WCF 自定义行为中动态更改 URL

Posted

技术标签:

【中文标题】如何在 WCF 自定义行为中动态更改 URL【英文标题】:How Dynamically change URL in a WCF Custom Behavior 【发布时间】:2017-01-10 16:06:14 【问题描述】:

类定义如下:

public class BizTalkRESTTransmitHandler : IClientMessageInspector

我是一个带有这个签名的方法:

public object BeforeSendRequest(ref Message request, IClientChannel channel)

所以我认为我需要操作通道对象。

原因是在 BizTalk 2010 SendPort 中使用它来支持 JSON。 到目前为止我试过这个:

if (channel.RemoteAddress.Uri.AbsoluteUri == "http://api-stage2.mypartner.com/rest/events/2/"
    || channel.RemoteAddress.Uri.AbsoluteUri == "http://api.mypartner.com/rest/events/2/")

    //TODO - "boxout" will become a variable obtained by parsing the message
    Uri newUri = new Uri(channel.RemoteAddress.Uri.AbsoluteUri + "boxout");
    channel.RemoteAddress.Uri = newUri; 


上面给出了编译错误:“System.ServiceModel.EndpointAddress.Uri”不能分配给 - 它只准备好了“RemoteAddress 似乎也是只读的。

我已经引用了这些问题,但它们不使用通道对象。 Assign a URL to Url.AbsoluteUri in ASP.NET,和 WCF change endpoint address at runtime 但他们似乎没有处理通道对象。

更新 1:我尝试了以下方法:

//try create new channel to change URL 
WebHttpBinding myBinding = new WebHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress(newURL);
ChannelFactory<IClientChannel> myChannelFactory = new ChannelFactory<IClientChannel>(myBinding, myEndpoint); //Change to you WCF interface
IClientChannel myNewChannel = myChannelFactory.CreateChannel();
channel = myNewChannel;  //replace the channel parm passed to us 

但它给出了这个错误: System.InvalidOperationException:尝试获取 IClientChannel 的合同类型,但该类型不是 ServiceContract,也不继承 ServiceContract。

【问题讨论】:

像这里这样创建一个全新的频道怎么样?好像有点矫枉过正。 ***.com/questions/27782919/… 【参考方案1】:

我可能有点晚了,但它会有所帮助。

我最近有一个类似的目标(也与 biztalk 有关),我需要根据消息中发送的某些值更改 url。 我尝试使用 ApplyDispatchBehavior 方法,但它从未被调用,而且我看不到如何从这里访问消息,所以我开始查看方法 BeforeSendRequest(在 Inspector 类中)。

这是我想出的:

object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
    
        var queryDictionary = HttpUtility.ParseQueryString(request.Headers.To.Query);
        string parameterValue = queryDictionary[this.BehaviourConfiguration.QueryParameter];

        //Only change parameter value if it exists
        if (parameterValue != null)
        
            MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);

            request = buffer.CreateMessage();

            //Necessary in order to read the message without having WCF throwing and error saying
            //the messas was already read
            var reqAux = buffer.CreateMessage();

            //For some reason the message comes in binary inside tags <Binary>MESSAGE</Binary>
            using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(reqAux.ToString().Replace("<Binary>", "").Replace("</Binary>", ""))))
            
                ms.Position = 0;
                string val = ExtractNodeValueByXPath(ms, this.BehaviourConfiguration.FieldXpath);

                queryDictionary.Set(this.BehaviourConfiguration.QueryParameter, DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" +
                    this.BehaviourConfiguration.Message + (string.IsNullOrWhiteSpace(val) ? string.Empty : "_" + val) + ".xml");

                UriBuilder ub = new UriBuilder(request.Headers.To);
                ub.Query = queryDictionary.ToString();
                request.Headers.To = ub.Uri;
            
        

        return null;
    

所以,我发现,弄乱request.Headers.To 我可以更改端点。

我在获取消息内容时遇到了几个问题,互联网上的大多数示例(显示使用 MessageBuffer.CreateNavigator 或 Message.GetBody 总是抛出我无法解决的异常)不会给我biztalk 消息而不是soap 消息?...不确定,但它有一个节点标头、正文和正文内部有一些base64 字符串,这不是我的biztalk 消息。

另外,正如您在Convert.FromBase64String(reqAux.ToString().Replace("&lt;Binary&gt;", "").Replace("&lt;/Binary&gt;", "")) 中看到的那样,我不得不做这个丑陋的替换。我不明白为什么这会出现在 base64 中,可能是一些 WCF 配置?但通过这样做,我可以寻找我的价值。

注意:我尚未对此进行全面测试,但到目前为止它适用于我的示例。

顺便说一句,我知道我可以用什么来切换我的 MemoryStream 使其成为更流媒体的解决方案吗?

【讨论】:

【参考方案2】:

IClientMessageInspector 不是操作频道的正确位置,您应该改用IEndpointBehavior

来自MSDN

实现可用于扩展运行时行为的方法 服务或客户端应用程序中的端点。

这是一个简单的例子:

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

    Uri endpointAddress = endpoint.Address.Uri;
    string address = endpointAddress.ToString();

    if (address == "http://api-stage2.mypartner.com/rest/events/2/"
    || address == "http://api.mypartner.com/rest/events/2/")
    
        //TODO - "boxout" will become a variable obtained by parsing the message
        Uri newUri = new Uri(address + "boxout");
        ServiceHostBase host = endpointDispatcher.ChannelDispatcher.Host;
        ChannelDispatcher newDispatcher = this.CreateChannelDispatcher(host, endpoint, newUri);
        host.ChannelDispatchers.Add(newDispatcher);
    

在这里您可以阅读 Carlos Figueira 关于IEndpointBehavior 的精彩帖子: https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/04/wcf-extensibility-iendpointbehavior/

另一种选择是使用 WCF 实现简单的路由,这里有一个示例链接: WCF REST service url routing based on query parameters

希望对你有帮助。

【讨论】:

获得了赏金......但遗憾的是,正如我告诉另一个回答的人,我需要根据消息中的数据更改 URL。我最终在 BizTalk Pipeline 中完成了它。【参考方案3】:

使用接口IEndpointBehavior,您将可以访问ApplyClientBehavior 方法,该方法公开ServiceEndPoint 实例。 现在您可以通过定义一个新的 EndpointAddress 实例来更改地址的值。

public class MyCustomEndpointBehavior : IEndpointBehavior
     
    public void AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    
    
    public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)
    
        serviceEndpoint.Address = new System.ServiceModel.EndpointAddress("http://mynewaddress.com");
    
    public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    
    
    public void Validate(ServiceEndpoint serviceEndpoint)
    
    

【讨论】:

我看到这可能适用于文字;但我没有在我的问题中解释——URL 需要基于消息类型的后缀。所以该消息似乎在该消息中不可用。 [我已经建立了一个 BizTalk Pipeline 可以做到这一点,但我宁愿它和 JSON/Conversion 一个程序,而不是两个。]

以上是关于如何在 WCF 自定义行为中动态更改 URL的主要内容,如果未能解决你的问题,请参考以下文章

WCF 自定义行为的依赖注入

如何构建包含 ListView.Builder 的自定义行,以在颤动中构建 ElevatedButton?

如何使用 CSS“nth-child”选择器选择自定义行数? [关闭]

如何在 androidleaback 库中自定义行的标题项?

如何在 Kendo.Scheduler 中自定义行或列颜色?

UITableViewCell 的自定义行动画