How Dynamically change URL in a WCF Custom Behavior

Class is defined as follows:

public class BizTalkRESTTransmitHandler : IClientMessageInspector

I'm a method with this signature:

public object BeforeSendRequest(ref Message request, IClientChannel channel)

So I think I need to manipulate the channel object.

The reason is this is being using in BizTalk 2010 SendPort to support JSON. I tried this so far:

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; 

}

Above gives compile error: "System.ServiceModel.EndpointAddress.Uri" cannot be assigned to - it is ready only" RemoteAddress seems to be read only as well.

I have referenced these questions but they don't use channel object. Assign a URL to Url.AbsoluteUri in ASP.NET, and WCF change endpoint address at runtime But they don't seem to be dealing with channel object.

Update 1: I tried the following:

//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 

but it gave this error: System.InvalidOperationException: Attempted to get contract type for IClientChannel, but that type is not a ServiceContract, nor does it inherit a ServiceContract.


IClientMessageInspector is not the right place the manipulate the Channel , you should use IEndpointBehavior instead:

From MSDN

Implements methods that can be used to extend run-time behavior for an endpoint in either a service or client application.

Here is a simple example:

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);
    }
}

Here you can read the excelent post of Carlos Figueira about IEndpointBehavior : https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/04/wcf-extensibility-iendpointbehavior/

Another alternative is to implement a simple Routing with WCF, here is link with an example: WCF REST service url routing based on query parameters

Hope it helps.


Using the interface IEndpointBehavior, you'll have access to the ApplyClientBehavior method, which exposes the ServiceEndPoint instance. Now you can change the value for the Address by defining a new EndpointAddress instance.

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)
    {
    }
}

I might be a bit too late but hoe it helps a bit.

I recently had a similar objective (also related to biztalk) where I needed to change the url based on some value sent on the message. I tried using the ApplyDispatchBehavior method but it was never called and also, I couldn't see how to access the message from here so I started looking at method BeforeSendRequest (in the Inspector class).

Here is what i came up with:

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;
    }

So, I discovered that, messing with the request.Headers.To I could change the endpoint.

I had several problems getting the message content and most examples on the internet (showing to use the MessageBuffer.CreateNavigator or Message.GetBody< string > which was always throwing an expcetion i couldn't get around) would not give me the biztalk message but rather the soap message?... not sure but it had a node header, body and inside the body there was some base64 string which was not my biztalk message.

Also, as you can see in Convert.FromBase64String(reqAux.ToString().Replace("<Binary>", "").Replace("</Binary>", "")) , I had to do this ugly replaces. I don't don't why this comes in base64, probably some WCF configuration?, but by doing it, I could then look for my value.

NOTE: I haven't fully tested this, but so far it as worked for my examples.

By the way, any idea on what can i switch my MemoryStream with so it becomes a more streaming solution?

链接地址: http://www.djcxy.com/p/36034.html

上一篇: Android上的Dagger 2。 存储和访问@Singleton组件的不同方式

下一篇: 如何在WCF自定义行为中动态更改URL