如何在 WCF 自定义行为中动态更改 URL
How Dynamically change URL in a WCF Custom Behavior
Class定义如下:
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。
使用接口 IEndpointBehavior, you'll have access to the ApplyClientBehavior method, which exposes the 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)
{
}
}
IClientMessageInspector
不是操纵 Channel 的正确位置,您应该改用 IEndpointBehavior
:
来自MSDN
Implements methods that can be used to extend run-time behavior for an
endpoint in either a service or client application.
这是一个简单的例子:
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
的精彩 post:
https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/04/wcf-extensibility-iendpointbehavior/
另一种方法是使用 WCF 实现简单的路由,这里是 link 示例:
希望对您有所帮助。
我可能来晚了一点,但还是有点帮助。
我最近有一个类似的 objective(也与 biztalk 相关),我需要根据消息发送的某些值更改 url。
我尝试使用 ApplyDispatchBehavior 方法,但它从未被调用过,而且我看不到如何从这里访问消息,所以我开始查看方法 BeforeSendRequest(在 Inspector class 中)。
这是我想出的:
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 消息?...不确定但它有一个节点 header、body 并且在 body 里面有一些 base64 字符串是不是我的 biztalk 消息。
此外,正如您在 Convert.FromBase64String(reqAux.ToString().Replace("<Binary>", "").Replace("</Binary>", ""))
中看到的那样,我不得不进行这种丑陋的替换。我不明白为什么这会出现在 base64 中,可能是一些 WCF 配置?但通过这样做,我可以寻找我的价值。
注意:我尚未对此进行全面测试,但到目前为止它对我的示例有效。
顺便问一下,我可以用什么来切换我的 MemoryStream 以使其成为更具流式传输的解决方案?
Class定义如下:
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。
使用接口 IEndpointBehavior, you'll have access to the ApplyClientBehavior method, which exposes the 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)
{
}
}
IClientMessageInspector
不是操纵 Channel 的正确位置,您应该改用 IEndpointBehavior
:
来自MSDN
Implements methods that can be used to extend run-time behavior for an endpoint in either a service or client application.
这是一个简单的例子:
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
的精彩 post:
https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/04/wcf-extensibility-iendpointbehavior/
另一种方法是使用 WCF 实现简单的路由,这里是 link 示例:
希望对您有所帮助。
我可能来晚了一点,但还是有点帮助。
我最近有一个类似的 objective(也与 biztalk 相关),我需要根据消息发送的某些值更改 url。 我尝试使用 ApplyDispatchBehavior 方法,但它从未被调用过,而且我看不到如何从这里访问消息,所以我开始查看方法 BeforeSendRequest(在 Inspector class 中)。
这是我想出的:
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
此外,正如您在 Convert.FromBase64String(reqAux.ToString().Replace("<Binary>", "").Replace("</Binary>", ""))
中看到的那样,我不得不进行这种丑陋的替换。我不明白为什么这会出现在 base64 中,可能是一些 WCF 配置?但通过这样做,我可以寻找我的价值。
注意:我尚未对此进行全面测试,但到目前为止它对我的示例有效。
顺便问一下,我可以用什么来切换我的 MemoryStream 以使其成为更具流式传输的解决方案?