none
wcf not friendly with Soap headers RRS feed

  • Question

  • Hi,

    I'm new to wcf.
    Reading documentation and this forum I'm getting to the conclusion that wcf is not friendly with soap headers.

    I've found two ways to work with headers, OperationContext class in a programatic fashion, but it have the problem that soapheader class isn't generate automatically from metadata in the client. Another way is using MessageContract that implies coding two clases for every method in the ServiceContract that use the header, which is a lot of work.

    Web services had a much more straightforward approach to soap headers. I'd like a SoapHeader attribute to decorate operations.

     

    Sunday, March 11, 2007 1:20 PM

Answers

  • I see now, yes, it is unfortunate that the MessageContract programming model is so 'heavy'... this has been a source of negative feedback from many people who must use MessageContract to exercise the finer control of the shape of the SOAP messages.  We are aware of this usability annoyance, perhaps this will improve in a future version of the framework.
    Wednesday, March 14, 2007 3:29 PM

All replies

  • What is the end result you are looking for?  I don't mean "to have SOAP headers".  I mean something like "to interop with a WS-Security SSL webservice".
    Monday, March 12, 2007 7:22 PM
  • I need:
    -T
    o interop with clients and services that use soap headers, asmx.
    -To use soap headers for the reason they were invented.

    msnd library says about SopaHeader class:
    "SOAP headers offer a method for passing data to and from an XML Web service method if the data is not directly related to the XML Web service method's primary functionality."

    If I have to pass general data to a stateless service, not related to a specific operation, but used by all of them, should I modify every single operation and add a parameter, or is better solution to pass a  soapheader?.
    What if that general data change, it is easier to modify every operation, every point in client code where operation is invoked, or just modify the soapheader? Soap headers make posible decouple general data needed by the service, from operation signatures, that is, the data needed for that specific operation. Both can change independently.
    That's one of my needs.


    But, do you agree with me, wcf is not friendly with soap headers?

     

    Monday, March 12, 2007 8:31 PM
  • I don't know enough about all the capabilities of [SoapHeader], but I think you can do similar stuff with WCF, below is some sample code.  On the client I can use AddressHeader in the EndpointAddress to apply a constant-value header to every message, or I can use OperationContext OutgoingHeaders to add headers on a per-call basis.  On server, I can use OperationContext IncomingHeaders to inspect them.


    using System;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Channels;

    // demo use of EndpointAddress reference parameters and OperationContextScope
    // to roughly mimic [SoapHeader]

    [ServiceContract]
    public interface IMyContract
    {
        [OperationContract]
        string echo(string s);
        [OperationContract]
        string echo2(string s);
    }

    public class MyService : IMyContract
    {
        public string echo(string s)
        {
            int i = OperationContext.Current.IncomingMessageHeaders.FindHeader("headerName", "http://headerNs");
            if (i != -1)
            {
                Console.WriteLine("header had value {0}", OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(i));
            }
            i = OperationContext.Current.IncomingMessageHeaders.FindHeader("otherHeaderName", "http://otherHeaderNs");
            if (i != -1)
            {
                Console.WriteLine("otherHeader had value {0}", OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(i));
            }
            Console.WriteLine("FYI, message on wire looked like {0}", OperationContext.Current.RequestContext.RequestMessage.ToString());
            return s;
        }
        public string echo2(string s)
        {
            int i = OperationContext.Current.IncomingMessageHeaders.FindHeader("headerName", "http://headerNs");
            if (i != -1)
            {
                Console.WriteLine("header had value {0}", OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(i));
            }
            return s;
        }
    }

    public class Repro
    {
        public static void Main()
        {
            Uri address = new Uri("http://localhost:8004/Service");
            Binding binding = new BasicHttpBinding();

            ServiceHost service = new ServiceHost(typeof(MyService), address);
            service.AddServiceEndpoint(typeof(IMyContract), binding, address);
            service.Open();

            // address headers get applied to every message
            ChannelFactory<IMyContract> cf = new ChannelFactory<IMyContract>(
                binding,
                new EndpointAddress(address, AddressHeader.CreateAddressHeader("headerName", "http://headerNs", "headerValue")));
            IMyContract proxy = cf.CreateChannel();
            using (new OperationContextScope((IClientChannel)proxy))
            {
                OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("otherHeaderName", "http://otherHeaderNs", "otherValue"));
                Console.WriteLine(proxy.echo("Hello World"));
            }
            Console.WriteLine(proxy.echo2("Hello World"));
            ((IClientChannel)proxy).Close();
            service.Close();
            Console.WriteLine("Done, press a key");
            Console.ReadKey();
        }
    }

     

    Tuesday, March 13, 2007 10:27 AM
  • Brian, thanks for replying.

    With operationContext it's posible to work with headers in code, not very friendly accessing two collections, but not so bad either. The big problem is that the header type is not defined in the contract, the header is not included in wsdl file, so clients have to call you by phone to ask for the type definition of the header, and you can read to them the code. That's not very friendly.

    Well, it is posible to define messageContracts and the header will be included in the wsld file. But you have to write two classes for every single operation that use the header. If you have 50 operations, you have to write 100 new classes just to use a little header, that's not friendly either.

    Wednesday, March 14, 2007 1:09 PM
  • I see now, yes, it is unfortunate that the MessageContract programming model is so 'heavy'... this has been a source of negative feedback from many people who must use MessageContract to exercise the finer control of the shape of the SOAP messages.  We are aware of this usability annoyance, perhaps this will improve in a future version of the framework.
    Wednesday, March 14, 2007 3:29 PM