Is there a better way to customize SOAP headers in C #

In the past, I had to create custom SOAP headers in a C # project that used an imported WSDL web reference. I found a way to do this, but I was never happy with it and I got the feeling that there is a better way. What I did was create a header that comes from SoapHeader:

[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://model.test.net")]
[System.Xml.Serialization.XmlRootAttribute("securitytoken", Namespace = "http://model.test.net", IsNullable = false)]
public class SpecialHeader : SoapHeader
{ 
  [System.Xml.Serialization.XmlTextAttribute()]
  public string aheadervalue;
}

      

Then I had to change the code that was generated from the WSDL and add a link before the new header instance and the following before every web call that I wanted to contain a custom header:

[System.Web.Services.Protocols.SoapHeaderAttribute("instancename", Direction=System.Web.Services.Protocols.SoapHeaderDirection.InOut)]

      

Where "instancename" is the name of the custom header variable in the generated class.

This works great, except that any change to the WSDL requires that the whole thing be redone since it rebuilds the class. In other languages, headers can be added outside of the generated code, so I may be missing a way that is done in C #. Are there any better ways to do this?

+1


source to share


4 answers


You seem to be using .Net 2.0 and asmx webservices. Did you know that there is a framework called WCF (Windows Communication Framework) in .NET 3.0. I know the transition to a new infrastructure is not easy, but with WCF you get so much. Also, WCf can be used for a lot more than WebServices (remote access, msmq, etc.). This is the foundation that Microsoft is banking on for the future. I.e. Soap header manipulation is done with MessageContracts.



So the answer is that in WCF you can do this with MessageContracts.

+3


source


The beacuse of the generated class is a partial class. You can define it in another file with the same namespace and class name (partial class again). Then you can override your virtual methods and define it once.

This prevents further changes to the regenerated class, does not affect the one you wrote.



In the new class file, you can use "GetWriterForMessage" to override and add new SOAP headers to it.

public partial class SampleService
{
    public string MessageID { get; set; }

    protected override System.Xml.XmlWriter GetWriterForMessage(System.Web.Services.Protocols.SoapClientMessage message, int bufferSize)
    {
        message.Headers.Add(new UsernameSoapHeader("Username"));
        message.Headers.Add(new PasswordSoapHeader("Password"));
        message.Headers.Add(new MessageIDSoapHeader(MessageID));
        return base.GetWriterForMessage(message, bufferSize);
    }
}

      

+2


source


There is a way to do this, like; it's not necessarily pretty, and on a very simple web service it might not be worth the effort, but it at least saves you the trouble of re-adding attributes when regenerating your code.

Since the generator generates partial classes, you can:

  • Add a file to the project that extends the web service class (the one derived from SoapHttpClientProtocol) with a different "partial" section (ie use the same namespace and name as the generated class and mark it with "partial" ").

  • Copy the methods by which you want to add the headers (i.e. the same methods you have already added) from the generated code and paste them into your extension section.

  • Rename the methods slightly so that they do not conflict with what was in the generated code, and change the names that are passed to the Invoke call. (You may need to tweak other method attributes as well to ensure they still map to the appropriate calls in the WSDL.)

  • Add your own header attribute to the renamed methods and the header instance field to your extension section.

  • Call the renamed versions from your code instead of the original versions.

As long as the method signatures are not changed in the WSDL, you don't have to change anything in your code, even if you regenerate. (Since you are only copying relatively short method implementations, any other structures from the WSDL will still come out of the generated code, so if they change, you will automatically get updated versions on regeneration. Of course, unless the WSDL has any other structures in it, the usefulness of this is probably somewhat limited.)

It's still not perfect, but it doesn't try to intercept the raw XML message and place the header directly (which you could probably do, but that would be disgusting), there really aren't any other options I know of (without going over to WCF).

0


source


I ran into this problem today. I ended up creating a class that comes from an autogenerated class and overrides the GetWriterForMessage method to ensure that my header is always present. I would update the header value on every method call.

0


source







All Articles