Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different SOAP Headers in request/response using Message Contracts

Tags:

wcf

I'm building some services which have a common header. This header has a certain layout in the request, and a different one in the response (i.e., there are two classes).

However, when I add a reference or use svcutil, the proxy is generated with the same header in both request and response types.

For instance:

[MessageContract]
class Contract<THeader, TBody>
{
    [MessageHeader] public THeader Header { get; set; }

    [MessageBodyMember] public TBody Body { get; set; }
}

class MyRequestHeader
{
    public string RequestorId { get; set; }
}

class MyResponseHeader
{
    public string ErrorMessage  { get; set; }
}

The OperationContract is something like:

[OperationContract]
public Contract<MyResponseHeader, ResponseBody> Process(Contract<MyRequestHeader, RequestBody> data);

The proxy becomes something like:

var client = new ...; 
var header = new MyRequestHeader(); 
var body = new RequestBody();

**ResponseBody** 

response = client.Process(ref header, body);

As you can see, the header (Request) is passed as ref; That probably means WCF is having this header as the same in request and response. And the MyResponseHeader disappears.

Anyone can shed some light on the subject?

like image 885
Bruno Brant Avatar asked May 16 '13 03:05

Bruno Brant


1 Answers

There is something strange going on here.

I tried to reproduce your problem, and got the following results (i had to mark some types public, and added [DataContract] to your header classes).

Here is a view of the WSDL:

WSDL

The generated code (svcutil 4.0.30319.18046) uses MyRequestHeader in the Response message as well:

Client code

This is caused by the following XSD:

XSD

As you can see there is only one instance generated for the "Header" class.

I tried creating types for the generic classes, as follows:

[MessageContract]
public abstract class Contract<THeader, TBody>
{
    [MessageHeader]
    public THeader Header { get; set; }

    [MessageBodyMember]
    public TBody Body { get; set; }
}

[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
    public string RequestorId { get; set; }
}

[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
    public string ErrorMessage { get; set; }
}

[MessageContract]
public class RequestContract : Contract<MyRequestHeader, string>
{ }

[MessageContract]
public class ResponseContract : Contract<MyResponseHeader, string>
{ }

[ServiceContract]
public interface IService1
{
    [OperationContract]
    ResponseContract Process(RequestContract data);
}

But that did not fix the problem, the generated client ResponseContract still is generated using a Header of type RequestHeader.

Even changing the service code to use two diffrent message contracts:

[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
    public string RequestorId { get; set; }
}

[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
    public string ErrorMessage { get; set; }
}

[MessageContract]
public class RequestContract<TBody>
{
    [MessageHeader]
    public MyRequestHeader Header { get; set; }

    [MessageBodyMember]
    public TBody Body { get; set; }
}

[MessageContract]
public class ResponseContract<TBody>
{
    [MessageHeader]
    public MyResponseHeader Header { get; set; }

    [MessageBodyMember]
    public TBody Body { get; set; }
}

[ServiceContract]
public interface IService1
{
    [OperationContract]
    ResponseContract<string> Process(RequestContract<string> data);
}

does not solve the problem:

Client code

Even removing all shared inheritance and generics as follows:

[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
    public string RequestorId { get; set; }
}

[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
    public string ErrorMessage { get; set; }
}

[MessageContract(WrapperName="RequestMessage")]
public class RequestContract
{
    [MessageHeader]
    public MyRequestHeader Header { get; set; }

    [MessageBodyMember]
    public string Body { get; set; }
}

[MessageContract(WrapperName = "ResponseMessage")]
public class ResponseContract
{
    [MessageHeader]
    public MyResponseHeader Header { get; set; }

    [MessageBodyMember]
    public string Body { get; set; }
}

[ServiceContract]
public interface IService1
{
    [OperationContract]
    ResponseContract Process(RequestContract data);
}

still results in the RequestHeader being used in the ResponseMessage.

I think the answer lies somewhere in this documentation:

WSDL Considerations

When generating a Web Services Description Language (WSDL) contract from a service that uses message contracts, it is important to remember that not all message contract features are reflected in the resulting WSDL [sic]. Consider the following points: WSDL cannot express the concept of an array of headers. When creating messages with an array of headers using the MessageHeaderArrayAttribute, the resulting WSDL reflects only one header instead of the array.

The resulting WSDL document may not reflect some protection-level information.

The message type generated in the WSDL has the same name as the class name of the message contract type.

When using the same message contract in multiple operations, multiple message types are generated in the WSDL document. The names are made unique by adding the numbers "2", "3", and so on, for subsequent uses. When importing back the WSDL, multiple message contract types are created and are identical except for their names.

like image 190
oɔɯǝɹ Avatar answered Oct 18 '22 22:10

oɔɯǝɹ