Search code examples
c#web-servicessoapwsdl

C# - Passing header information to SOAP webservice client


I've added a SOAP webservice as a Service Reference in a C# project in Visual Studio, but either I'm doing something wrong or it doesn't seem to be properly parsed. The WSDL clearly exposes a header to pass an authentication token (which I can get from another method), and this header is referenced in the method I need to use (getDeviceInfoRequest). Relevant WSDL bits below:

<wsdl:message name="getDeviceInfoRequest">
<wsdl:part name="Auth" type="types:Auth"/>
<wsdl:part name="DeviceName" type="xsd:string"/>
</wsdl:message>

<wsdl:operation name="getDeviceInfo">
<wsdl:input name="getDeviceInfoRequest" message="tns:getDeviceInfoRequest"/>
<wsdl:output name="getDeviceInfoResponse" message="tns:getDeviceInfoResponse"/>
</wsdl:operation>

<wsdl:operation name="getDeviceInfo">
<soap:operation soapAction="" style="rpc"/>
<wsdl:input name="getDeviceInfoRequest">
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:NetworkService" use="encoded" parts="DeviceName"/>
<soap:header encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:NetworkService" use="encoded" message="tns:getDeviceInfoRequest" part="Auth"/>
</wsdl:input>
<wsdl:output name="getDeviceInfoResponse">
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:NetworkService" use="encoded"/>
</wsdl:output>
</wsdl:operation>

<xsd:complexType name="Auth">
<xsd:sequence>
<xsd:element name="token" nillable="false" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

However, when I generate the client proxy in Visual Studio (References -> Add Service Reference) there is no way to pass the token to the getDeviceInfoRequest method, as it is generated with a single parameter (DeviceName). Is this a problem with the parsing of the WSDL file or am I looking at it the wrong way and there is a completely different way to set the headers on the requests?

Thanks!


Solution

  • I'm not sure this is the proper way to do it, but in the end I managed by creating a dedicated serializable class for authentication and adding a custom header to the request.

            LanDB.NetworkServiceInterfaceClient client = new LanDB.NetworkServiceInterfaceClient();
            String token = client.getAuthToken("user", "name", "domain");
            Auth tokenAuth = new Auth(token);
            LanDB.DeviceInfo selectedPLCInfo = new LanDB.DeviceInfo();
    
            // Add a SOAP autentication Header (Header property in the envelope) to the outgoing request.
            using (new OperationContextScope(client.InnerChannel))
            {
                MessageHeader aMessageHeader = MessageHeader.CreateHeader("Auth", "", tokenAuth);
                OperationContext.Current.OutgoingMessageHeaders.Add(aMessageHeader);
                selectedPLCInfo = client.getDeviceInfo("plcHostname");
            }
    

    With the class being

    [DataContract]
    public class Auth
    {
    
        [DataMember]
        string token;
        public Auth(string value)
        {
            token = value;
        }
    }
    

    This way the XML request is properly built and sent.

    Additionally, none of this is required if I add the service as a Web Service instead of a Service Reference. In that case I get an object in the client that I can set (AuthValue) with the correct token and the client code handles everything. Go figure!