locked
Sending object in header? RRS feed

  • Question

  • Hi,

    I have implemented IDispatchMessageInspector, IParameterInspector and BehaviorExtensionElement on my WCF service to be able to analys header data on every call(Much like this ).

    I  do however have two questions :

    1. I need to attach a datacontract to the message header on client side before send and I have tried this :

          using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))<br/>
          {<br/>
            MessageHeader<IntegrationHeader> mhg = new MessageHeader<IntegrationHeader>();<br/>
    <br/>
            mhg.Content = new IntegrationHeader();<br/>
            mhg.Content.LoginName = "MyUser";<br/>
            mhg.Content.Password = "MyPassword";<br/>
    <br/>
            MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");<br/>
            OperationContext.Current.OutgoingMessageHeaders.Add(untyped);<br/>
    <br/>
            client.GetVersion(out one, out two);<br/>
          }
    

    But when running GetUntypedHeader a null reference will be thrown, this works however if I change MessageHeader<IntegrationHeader> to MessageHeader<String> ?

    2. The header will be analysed in the Service > IDispatchMessageInspector > AfterReceiveRequest, but how do I extract the object?

    BestRegards

     

    Monday, February 28, 2011 9:21 AM

Answers

  • Also, another way to do it is to not use the typed message header, instead simply pass the object you want in the header to MessageHeader.CreateHeader:

          using (new OperationContextScope((IContextChannel)proxy))
          {
            IntegrationHeader ih = new IntegrationHeader { LoginName = "john doe", Password = "******" };
            MessageHeader untyped = MessageHeader.CreateHeader("token", "ns", ih);
            OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
            Console.WriteLine(proxy.Echo("Hello"));
          }
    
    
    • Marked as answer by SnowJim Thursday, March 3, 2011 7:51 AM
    Monday, February 28, 2011 5:32 PM

All replies

  • Maby I need to manually serlize(on client) and deserilize(on service) to get this working? This would then mean that I am handling the extra header data as a string when its really should be a datacontract object?

    Is this the right(only) way?

    Monday, February 28, 2011 10:45 AM
  • When you create a typed message header you need to specify an "actor" name for the header, otherwise the serializer complains about it (frankly I don't know why on the <string> case it works). I identified the problem by looking at the exception message which was thrown when I didn't set a value for that property (see below).

    Unhandled Exception: System.ArgumentNullException: Value cannot be null.
    Parameter name: actor
       at System.ServiceModel.Channels.XmlObjectSerializerHeader..ctor(XmlObjectSerializer serializer, Boolean mustUnderstand, String actor, Boolean relay)
       at System.ServiceModel.Channels.XmlObjectSerializerHeader..ctor(String name, String ns, Object objectToSerialize, XmlObjectSerializer serializer, Boolean mustUnderstand, String actor, Boolean relay)
       at System.ServiceModel.MessageHeader`1.GetUntypedHeader(String name, String ns)
       at ConsoleApplication1.Post_5dddcdad_3c74_43f6_9c04_fb69cf2c6f77.Test()
       at ConsoleApplication1.MainClass.Main(String[] args)

    If you don't want an actor attribute in the header (which is the most common case), you can specify the empty string:

    using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
    {
        MessageHeader<IntegrationHeader> mhg = new MessageHeader<IntegrationHeader>();

        mhg.Content = new IntegrationHeader();
        mhg.Content.LoginName = "MyUser";
        mhg.Content.Password = "MyPassword";
        mhg.Actor = "";
        MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
        OperationContext.Current.OutgoingMessageHeaders.Add(untyped);

        client.GetVersion(out one, out two);
    }

    Below is my full code for this problem:

      public class Post_5dddcdad_3c74_43f6_9c04_fb69cf2c6f77
      {
        [ServiceContract]
        public interface ITest
        {
          [OperationContract]
          string Echo(string text);
        }
        [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
        public class Service : ITest
        {
          public string Echo(string text)
          {
            return text;
          }
        }
        public class MyInspector : IEndpointBehavior, IDispatchMessageInspector
        {
          public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
          {
          }
    
          public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
          {
          }
    
          public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
          {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
          }
    
          public void Validate(ServiceEndpoint endpoint)
          {
          }
    
          public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
          {
            foreach (var header in request.Headers)
            {
              Console.WriteLine("{0}: {1}", header.Name, header.Namespace);
              if (header.Namespace == "ns" && header.Name == "token")
              {
                IntegrationHeader typed = request.Headers.GetHeader<IntegrationHeader>(header.Name, header.Namespace);
                Console.WriteLine(" {0}", typed);
              }
            }
            return null;
          }
    
          public void BeforeSendReply(ref Message reply, object correlationState)
          {
          }
        }
        [DataContract]
        public class IntegrationHeader
        {
          [DataMember]
          public string LoginName { get; set; }
          [DataMember]
          public string Password { get; set; }
          public override string ToString()
          {
            return string.Format("IntegrationHeader[LoginName={0},Password={1}]", LoginName, Password);
          }
        }
        public static void Test()
        {
          string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
          ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
          ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
          endpoint.Behaviors.Add(new MyInspector());
          host.Open();
          Console.WriteLine("Host opened");
    
          ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
          ITest proxy = factory.CreateChannel();
    
          using (new OperationContextScope((IContextChannel)proxy))
          {
            MessageHeader<IntegrationHeader> mhg = new MessageHeader<IntegrationHeader>();
            mhg.Content = new IntegrationHeader { LoginName = "john doe", Password = "******" };
            mhg.Actor = "";
            MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
            OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
            Console.WriteLine(proxy.Echo("Hello"));
          }
    
          ((IClientChannel)proxy).Close();
          factory.Close();
    
          Console.Write("Press ENTER to close the host");
          Console.ReadLine();
          host.Close();
        }
      }
    
    
    Monday, February 28, 2011 5:29 PM
  • Also, another way to do it is to not use the typed message header, instead simply pass the object you want in the header to MessageHeader.CreateHeader:

          using (new OperationContextScope((IContextChannel)proxy))
          {
            IntegrationHeader ih = new IntegrationHeader { LoginName = "john doe", Password = "******" };
            MessageHeader untyped = MessageHeader.CreateHeader("token", "ns", ih);
            OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
            Console.WriteLine(proxy.Echo("Hello"));
          }
    
    
    • Marked as answer by SnowJim Thursday, March 3, 2011 7:51 AM
    Monday, February 28, 2011 5:32 PM
  • Thanks alot! That works fine.

    I do however wonder a thing, what does the xmlns="ns" stand for? what is its purpose? and is the xmlns really necessary?

    BestRegards

    Thursday, March 3, 2011 7:51 AM
  • Each element in XML is identified by two values, its name and its namespace. The "ns" in this case is the namespace of the header element. If you don't want it, simpy pass "" to the namespace.

    Thursday, March 3, 2011 3:11 PM
  • Thanks!
    Thursday, March 3, 2011 5:18 PM