How to name soap operation differently from the related requestDTO

One of our consumer would like to have the soap/wsdl operation named differently from requestDTO.
Given the following scenario:

//RequestDTO
public class GetCountries
{
	[DataMember]
	[ApiMember(Name = "Ids", Description = "Ids Description",
	  ParameterType = "path", DataType = "int", IsRequired = true, AllowMultiple = true)]
	public int[] Ids { get; set; }

	public GetCountries()
	{
		Ids = new int[0];
	}
}

//ResponseDTO
[DataContract]
public class GetCountriesResponse
{
	public GetCountriesResponse()
	{
		Countries = new List<Country>();
	}

	[DataMember]
	public List<Country> Countries { get; set; }
}

we end up with the following wsdl defition

<xs:schema xmlns:tns="http://acme.org/myservice/types" elementFormDefault="qualified" targetNamespace="http://acme.org/myservice/types" xmlns:xs="http://www.w3.org/2001/XMLSchema">
...
	<xs:complexType name="GetCountries">
	<xs:sequence>
	  <xs:element minOccurs="0" name="Ids" nillable="true" xmlns:q4="http://schemas.microsoft.com/2003/10/Serialization/Arrays" type="q4:ArrayOfint" />
	</xs:sequence>
	</xs:complexType>
	<xs:element name="GetCountries" nillable="true" type="tns:GetCountries" />
	<xs:complexType name="GetCountriesResponse">
	<xs:sequence>
	  <xs:element minOccurs="0" name="Countries" nillable="true" type="tns:ArrayOfCountry" />
	</xs:sequence>
	</xs:complexType>
	<xs:element name="GetCountriesResponse" nillable="true" type="tns:GetCountriesResponse" />
...
</xs:schema>

<wsdl:message name="GetCountriesIn">
	<wsdl:part name="par" element="tns:GetCountries" />
</wsdl:message>
<wsdl:message name="GetCountriesOut">
	<wsdl:part name="par" element="tns:GetCountriesResponse" />
</wsdl:message>

<wsdl:portType name="ISyncReply">
    <wsdl:operation name="GetCountries">
        <wsdl:input message="svc:GetCountriesIn" />
        <wsdl:output message="svc:GetCountriesOut" />
    </wsdl:operation>
	...
</wsdl:portType>	

<wsdl:binding name="WSHttpBinding_ISyncReply" type="svc:ISyncReply">
	<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
	<wsdl:operation name="GetCountries">
		<soap:operation soapAction="http://acme.org/myservice/types/GetCountries" style="document" />
		  <wsdl:input>
			<soap:body use="literal" />
		  </wsdl:input>
		  <wsdl:output>
			<soap:body use="literal" />
		</wsdl:output>
	</wsdl:operation>	
	...
</wsdl:binding>

all that leads to the following proxy signature(generated w/ svcutil)

public GetCountriesResponse GetCountries(GetCountries GetCountries1){..}

Our consumer would like to have instead (he don’t waht have the proxy method named same as the requestDTO type)

public GetCountriesDTOResponse GetCountries(GetCountriesDTO GetCountries1){..}

As far as I understood, the soap “operation” is totally transparent to the SS framework: the only thing that matter for SS is the DTO.
So I can name the wsdl operation as I wish and that won’t impact the SS as long as the DTO is consistent with the ServiceModel(RequestDTO)
My idea to accomplish his request is based on the following step:

  • Rename the DTOs adding the “DTO” string within the name: GetCountries->GetCountriesDTO, GetCountriesResponse->GetCountriesResponseDTO

  • use the GenerateWsdl override to customize the wsdl (rename the operation)

    public override string GenerateWsdl(WsdlTemplateBase wsdlTemplate)
    {
    var wsdl = base.GenerateWsdl(wsdlTemplate);
    string input = wsdl;
    string pattern = “<wsdl:operation name=”(?.*?)DTO">";
    string replacement = “<wsdl:operation name=”${operationName}">";
    string result = Regex.Replace(input, pattern, replacement);
    return result;

    }

The types within the xs:schema will be renamed contianing the “DTO” string(I’ll skip all the xml for brevity)

<wsdl:message name="GetCountriesDTOIn">
	<wsdl:part name="par" element="tns:GetCountriesDTO" />
</wsdl:message>
<wsdl:message name="GetCountriesDTOOut">
	<wsdl:part name="par" element="tns:GetCountriesDTOResponse" />
</wsdl:message>

<wsdl:portType name="ISyncReply">
	<wsdl:operation name="GetCountries">
        <wsdl:input message="svc:GetCountriesDTOIn" />
        <wsdl:output message="svc:GetCountriesDTOOut" />
    </wsdl:operation>
	...
	
<wsdl:binding name="WSHttpBinding_ISyncReply" type="svc:ISyncReply">
	<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
	<wsdl:operation name="GetCountries">
      <soap:operation soapAction="http://acme.org/myservice/types/GetCountriesDTO" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
...	

The only point left is the documentation / metadata page: after the DTO rename, the “Operations” listed within the page will be named with the suffix DTO.
A quick&dirty solution could be create a custom html template injecting some javascript to rename the operation name removing the DTO.
I wonder if ther’s a better solution for that: is there any server-side hook to perform that rename?

Firstly I’d warn against this being a very fragile approach, WCF/WSDL/XSD’s are already extremely fragile/brittle to begin with that trying to alter their natural representation is likely going to cause other issues in future. Modifying the WSDL is not enough you’ll also need to ensure any modifications are not incompatible with the WCF SOAP Message deserializing SOAP requests. My first instinct is to look to see if the code-generation tool you’re using can be modified to support the style you want without altering the WSDL.

You may also want to consider evaluating the Java Native Types Support that was just released in ServiceStack v4.0.40 to see if that would be more suitable option given SOAP/WSDL’s is generally a fragile/verbose/slow option for data interchange.

If you still want to consider renaming DTO’s you can look at adding a custom metadata page filter to change the model used in metadata pages, otherwise you can replace the entire Metadata Page Template by adding a modified copy of the metadata templates in your project.

I agree with you: I’d prefer to avoid to modify the wsdl.

I noticed the SS wsdl defines the DTOs (request and response) as following

<xs:complexType name="GetCountries">
<xs:sequence>
  <xs:element minOccurs="0" name="Ids" nillable="true" xmlns:q4="http://schemas.microsoft.com/2003/10/Serialization/Arrays" type="q4:ArrayOfint" />
</xs:sequence>
</xs:complexType>
<xs:element name="GetCountries" nillable="true" type="tns:GetCountries" />

having a dedicated empty node xs:element and a different xs:complexType node for the same DTO

I noticed in other wsdls (generated by other frameworks) the xs:complexType has been wrapped/included as child of the xs:element as following:

<xs:element name="GetCountries" nillable="true" type="tns:GetCountries">
	<xs:complexType name="GetCountries">
	<xs:sequence>
	  <xs:element minOccurs="0" name="Ids" nillable="true" xmlns:q4="http://schemas.microsoft.com/2003/10/Serialization/Arrays" type="q4:ArrayOfint" />
	</xs:sequence>
	</xs:complexType>
</xs:element>

using that wsdl(all other parts including port, message and binding are exactly the same as SS wsdl) the generated proxy class creates a dedicated type for the input parameter named “Request”

public GetCountriesResponse GetCountries(GetCountriesRequest GetCountries1){..}

that is exaclty what I’m looking for. Do you know if that DTO schema definition (having the xs:complex type wrapped in the releated xs:element) can be accomplished using same dedicated property of the System.Runtime.Serialization.DataContractAttribute or how can be done?

The [DataContract] and [DataMember] attributes provide very little in the way of customization so I doubt this is supported.

If it’s just a matter of how the code-gen looks in Java could you maybe write a tool that looks at the generated proxy and generates an encapsulating desired proxy which calls into the WSDL-generated proxy? Basically generate new Request DTO’s for each operation that just inherits the original Request DTO and calls the internal proxy, e.g. something like:

public class GetCountriesRequest extends GetCountries {}

...
public class MyCustomGateway
{
    ApacheCxfGeneratedProxy proxy = ...;

    public GetCountriesResponse GetCountries(GetCountriesRequest request){
        return proxy.GetCountries(request);
    }
}

Generating a custom gateway like this is going to offer more control and less likely break the Service Contract. It’s not ideal but there’s less that can go wrong with customizing code then customizing WSDL’s.

After some additional research I agree with you the solution is working on the client side(proxy generator) not on the server side wsdl.

Althought I founded that interesting article

simply renaming the attribute name from “par”(generated by SS wsdl) to “parameters”

 <wsdl:part name="par" ,,,

using svcutil you can decide (using the /wrapped option) to wrap or not the DTOs (Request and Response)

using name=“parameters” with no /wrapped option you get

CountryServiceProxy.Country[] GetCountries(int[] Ids, CountryServiceProxy.QueryObject Query);

where input and output are the content/properties of the related DTOs(GetCountries and GetCountriesResponse)

using name=“parameters” with /wrapped option you get

CountryServiceProxy.GetCountriesResponse1 GetCountries(CountryServiceProxy.GetCountriesRequest request);

using name=“par” you only get the wrapped version

1 Like