Как динамически изменять URL-адрес в пользовательском поведении WCF

класс определяется следующим образом:

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; 

}

выше дает ошибку компиляции: "система.Средство servicemodel.Адрес endpointaddress.Uri "не может быть назначен - он готов только" RemoteAddress, кажется, только для чтения.

я ссылался на эти вопросы, но они не используют объект канала. назначьте URL-адрес Url-адресу.AbsoluteUri in ASP.NET, и WCF изменить адрес конечной точки во время выполнения Но они, похоже, не имеют дело с объектом канала.

обновление 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 

но это дало эту ошибку: Система.InvalidOperationException: попытка получить тип контракта для IClientChannel, но этот тип не является ServiceContract, ни наследует ли он ServiceContract.

3 ответов


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

здесь вы можете прочитать отличный пост Карлоса Фигейры о IEndpointBehavior: https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/04/wcf-extensibility-iendpointbehavior/

Другой альтернативой является реализация простой маршрутизации с WCF, вот ссылка с примером: маршрутизация url-адресов службы WCF REST на основе параметров запроса

надеюсь, что это помогает.


через интерфейс 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)
    {
    }
}

Я, возможно, немного опоздал, но мотыга это немного помогает.

недавно у меня была аналогичная цель (также связанная с 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 или сообщение.GetBody, который всегда бросал expcetion я не мог обойти) не дал бы мне сообщение biztalk, а сообщение soap?... не уверен, но у него был заголовок узла, тело и внутри тела была строка base64, которая не была моей biztalk сообщение.

кроме того, как вы можете видеть в Convert.FromBase64String(reqAux.ToString().Replace("<Binary>", "").Replace("</Binary>", "")), Я должен был сделать это уродливое заменяет. Я не знаю, почему это происходит в base64, возможно, в какой-то конфигурации WCF? но, сделав это, я смогу найти свою ценность.

примечание: Я не полностью протестировал это, но до сих пор это работало для моих примеров.

кстати, любая идея о том, что я могу переключить свой MemoryStream, чтобы он стал более потоковым решением?