以编程方式将自定义 WCF 标头添加到端点以实现可靠会话

发布于 2024-09-08 02:14:25 字数 3633 浏览 3 评论 0原文

我正在构建一个 WCF 路由器,我的客户端使用可靠会话。在这种情况下,当客户端打开通道时,会发送一条消息(建立可靠会话?)。其内容如下:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</a:Action>
    <a:MessageID>urn:uuid:1758f794-c5d3-4573-b252-7a07344cc257</a:MessageID>
    <a:To s:mustUnderstand="1">http://localhost:8010/RouterService</a:To>
  </s:Header>
  <s:Body>
    <CreateSequence xmlns="http://docs.oasis-open.org/ws-rx/wsrm/200702">
      <AcksTo>
        <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </AcksTo>
      <Offer>
        <Identifier>urn:uuid:64a12658-71d9-4967-88ec-9bb0610f7ecb</Identifier>
        <Endpoint>
          <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
        </Endpoint>
        <IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
      </Offer>
    </CreateSequence>
  </s:Body>
</s:Envelope>

这里的问题是标头不包含任何我可以用来查找将消息路由到哪个服务的信息。在 Busatmante 的路由器示例代码中,她通过向端点添加标头来解决此问题:

  <client>
    <endpoint address="http://localhost:8010/RouterService" binding="ws2007HttpBinding"
      bindingConfiguration="wsHttp"
      contract="localhost.IMessageManagerService" >
      <headers>
        <Route xmlns="http://www.thatindigogirl.com/samples/2008/01" >http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
      </headers>
    </endpoint>
  </client>

当打开可靠会话时,消息包含此自定义标头。

<Route a:IsReferenceParameter="true" xmlns="http://www.thatindigogirl.com/samples/2008/01">http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>

这太棒了;但是,我需要以编程方式配置客户端。我认为 ChannelFactory Endpoint 将有一个 Header 对象,我可以手动添加自定义标头。不幸的是事实并非如此。因此,我做了一些搜索,发现了一些通过实现 IClientMessageInspector 来添加我的标头并将其作为行为添加到我的端点来扩展 WCF 的建议。

public class ContractNameMessageInspector : IClientMessageInspector {

    private const string HEADER_NAME = "ContractName";
    private readonly string _ContractName;

    public ContractNameMessageInspector(string contractName) {
        _ContractName = contractName;
    }

    #region IClientMessageInspector Members

    public void AfterReceiveReply(ref Message reply, object correlationState) { }

    public object BeforeSendRequest(ref Message request, IClientChannel channel) {

        HttpRequestMessageProperty httpRequestMessage;
        object httpRequestMessageObject;

        if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) {
            httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
            if (httpRequestMessage != null && string.IsNullOrEmpty(httpRequestMessage.Headers[HEADER_NAME])) {
                httpRequestMessage.Headers[HEADER_NAME] = this._ContractName;
            }
        }
        else {
            httpRequestMessage = new HttpRequestMessageProperty();
            httpRequestMessage.Headers.Add(HEADER_NAME, this._ContractName);
            request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
        }
        return null;
    }

    #endregion
}

因此,当我的客户端进行服务调用时,消息包含自定义标头,但建立可靠会话的消息仍然不包含。

所以我的问题是;如何以编程方式将自定义标头添加到端点,以便可靠会话消息包含它?

非常感谢

I'm building a WCF router and my client uses Reliable Sessions. In this scenario when the client opens a channel a message is sent (establishing a Reliable Session?). Its contents is as follows:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</a:Action>
    <a:MessageID>urn:uuid:1758f794-c5d3-4573-b252-7a07344cc257</a:MessageID>
    <a:To s:mustUnderstand="1">http://localhost:8010/RouterService</a:To>
  </s:Header>
  <s:Body>
    <CreateSequence xmlns="http://docs.oasis-open.org/ws-rx/wsrm/200702">
      <AcksTo>
        <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </AcksTo>
      <Offer>
        <Identifier>urn:uuid:64a12658-71d9-4967-88ec-9bb0610f7ecb</Identifier>
        <Endpoint>
          <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
        </Endpoint>
        <IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
      </Offer>
    </CreateSequence>
  </s:Body>
</s:Envelope>

The problem here is that the headers do not contain any information I can use to look up what service to route the message to. In Busatmante's router sample code she gets around this by adding a header to the endpoint:

  <client>
    <endpoint address="http://localhost:8010/RouterService" binding="ws2007HttpBinding"
      bindingConfiguration="wsHttp"
      contract="localhost.IMessageManagerService" >
      <headers>
        <Route xmlns="http://www.thatindigogirl.com/samples/2008/01" >http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
      </headers>
    </endpoint>
  </client>

When the reliable session is opened the message contains this custom header.

<Route a:IsReferenceParameter="true" xmlns="http://www.thatindigogirl.com/samples/2008/01">http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>

This is great; however, I have a requirement to configure the client programatically. I figured that the ChannelFactory Endpoint would have a Header object to which I could manually add my custom header. Unfortunately it does not. So I did some searching and found some recomendations to extend WCF by implementing an IClientMessageInspector to add my header and adding it as a behavior to my endpoint.

public class ContractNameMessageInspector : IClientMessageInspector {

    private const string HEADER_NAME = "ContractName";
    private readonly string _ContractName;

    public ContractNameMessageInspector(string contractName) {
        _ContractName = contractName;
    }

    #region IClientMessageInspector Members

    public void AfterReceiveReply(ref Message reply, object correlationState) { }

    public object BeforeSendRequest(ref Message request, IClientChannel channel) {

        HttpRequestMessageProperty httpRequestMessage;
        object httpRequestMessageObject;

        if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) {
            httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
            if (httpRequestMessage != null && string.IsNullOrEmpty(httpRequestMessage.Headers[HEADER_NAME])) {
                httpRequestMessage.Headers[HEADER_NAME] = this._ContractName;
            }
        }
        else {
            httpRequestMessage = new HttpRequestMessageProperty();
            httpRequestMessage.Headers.Add(HEADER_NAME, this._ContractName);
            request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
        }
        return null;
    }

    #endregion
}

So when my client makes a service call the message contains the custom header but the message establishing the Reliable Sessions still does not.

So my question is; how do I add a custom header to the Endpoint programatically in such a way that the reliable session message contains it?

Many Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦途 2024-09-15 02:14:25

想通了。尽管没有属性或方法可以将标头添加到 EndpointAddress,但构造函数上有一个可选参数。

_Binding = bindingFactory.GetBinding(serviceUri, typeof(T));
AddressHeader header = AddressHeader.CreateAddressHeader("ContractName", "NameSpace", typeof (T).ToString());

_Address = new EndpointAddress(serviceUri, header);
_ChannelFactory = new ChannelFactory<T>(_Binding, _Address);

现在,当我收到建立可靠会话的消息时,它实际上包含我的自定义标头。这是有道理的,因为消息检查器很可能只对分派的消息进行操作,而建立可靠会话的消息是由较低级别的 WCF 代码生成的。

Figured it out. Although there is no property or method to add a header to an EndpointAddress there is an optional parameter on the constructor.

_Binding = bindingFactory.GetBinding(serviceUri, typeof(T));
AddressHeader header = AddressHeader.CreateAddressHeader("ContractName", "NameSpace", typeof (T).ToString());

_Address = new EndpointAddress(serviceUri, header);
_ChannelFactory = new ChannelFactory<T>(_Binding, _Address);

Now when I receive the message establishing the reliable session it actually does contain my custom header. This kinda makes sense as the message inspector most likely only operates on dispatched messages while the message establishing the reliable session is generated by lower level WCF code.

陌伤浅笑 2024-09-15 02:14:25

这对我有用

   public TResult Invoke<TResult>(Func<TClient, TResult> func,MessageHeader header)
    {
        TClient client = default(TClient);
        var sw = new Stopwatch();
        try
        {
            sw.Start();
            using (client = _channelFactory.CreateChannel())
            {
               using (OperationContextScope contextScope = new OperationContextScope(client))
                {
                    OperationContext.Current.OutgoingMessageHeaders.Add(header);

                    return func.Invoke(client);
                }
            }
        }
        finally
        {
            CloseConnection(client);
            Instrument(this.GetType().Name, sw);
        }
    }

This works for me

   public TResult Invoke<TResult>(Func<TClient, TResult> func,MessageHeader header)
    {
        TClient client = default(TClient);
        var sw = new Stopwatch();
        try
        {
            sw.Start();
            using (client = _channelFactory.CreateChannel())
            {
               using (OperationContextScope contextScope = new OperationContextScope(client))
                {
                    OperationContext.Current.OutgoingMessageHeaders.Add(header);

                    return func.Invoke(client);
                }
            }
        }
        finally
        {
            CloseConnection(client);
            Instrument(this.GetType().Name, sw);
        }
    }
慕烟庭风 2024-09-15 02:14:25

要以编程方式添加地址标头,请参阅 MSDN 的 地址标头 其中可以以编程方式添加标头,例如:

var cl = new MyWCFClientContext();

var eab = new EndpointAddressBuilder(cl.Endpoint.Address);

eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientIdentification", // Header Key
                                                    string.Empty,           // Namespace
                                                    "JabberwockyClient"));  // Header Value

cl.Endpoint.Address = eab.ToEndpointAddress();

To programmatically add address headers see MSDN's Address Headers where one can programmatically add a header such as:

var cl = new MyWCFClientContext();

var eab = new EndpointAddressBuilder(cl.Endpoint.Address);

eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientIdentification", // Header Key
                                                    string.Empty,           // Namespace
                                                    "JabberwockyClient"));  // Header Value

cl.Endpoint.Address = eab.ToEndpointAddress();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文