Search code examples
c#web-servicessoapws-securityblueprism

C# Runtime Error when implementing WSSE Security Headers with custom fields in SOAP request


I am trying to send a SOAP request to a web service that uses WSSE and UsernameToken for authentication. The sample query is as follows (masking confidential data):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:kas="http://webservice.com">
        <soapenv:Header>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
         <wsse:UsernameToken>
            <wsse:Username>abc</wsse:Username>
            <wsse:CustomField>123</wsse:CustomField>
         </wsse:UsernameToken>
      </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <kas:method1>
         <!--Optional:-->
         <method1>
            <!--Optional:-->
            <queryNo>12345678901</queryNo>
         </method1>
      </kas:method1>
   </soapenv:Body>
</soapenv:Envelope> 

I have generated a proxy class using WSE 3.0 and the problem is that I get the error: "Object reference not set to an instance of an object." The problematic part of my C# code is below:

queryNoSorguType q = new queryNoSorguType();
string query_parameter = query_no;
q.queryNo = query_parameter;

ResultType[] r = new ResultType[10];

UsernameToken token = new UsernameToken("abc", "123",PasswordOption.SendPlainText);
//mWebService.SetClientCredential<UsernameToken>(token);
//Policy webServiceClientPolicy = new Policy();
mWebService.RequestSoapContext.Security.Tokens.Add(token);
//mWebService.SetPolicy(webServiceClientPolicy);

//r = mWebService.documentQuerybyQueryNo(q);

System.Data.DataTable outputDataTable = new System.Data.DataTable();
//System.Data.DataRow outRow = outputDataTable.Rows.Add();
//outRow["field1"] = r;
output = outputDataTable;

I located the problematic part by systemically commenting out portions of my code. I am quite unfamiliar with web services, C# and I am actually implementing this in Blue Prism. Although this program works with SOAP web services out of the box, unfortunately it does not natively support SOAP headers.

The SOAP request works fine in SOAP UI and there are no compiler errors in Blue Prism. I tried adding the headers as instructed in the manual and on the web, but it did not work. I would appreciate it if you could point me in the right direction.

EDIT After writing, compiling a console application in Visual Studio 2017 I get the following error. As far as I understand it does not have the definitions for the headers.

Unhandled Exception: System.Web.Services.Protocols.SoapHeaderException: MustUnderstand headers:[{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood
   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   at WebService.queryByQueryNo(queryNoQueryType queryByQueryNo1) in C:\Users\user\source\repos\ConsoleApp1\ConsoleApp1\Web References\WebService\Reference.cs:line 1533
   at ConsoleApp1.Program.Main(String[] args) in C:\Users\user\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 33

Solution

  • I decided to utilize a different method and quit trying to use a proxy class for the time being as there were problems associated with it. Making use of the answers on this link: Client to send SOAP request and receive response I came up with my own solution after some customization.

    However, I still wonder how to proceed in order to make it work using wrapper classes defined by Visual Studio or WSE 3.0. After writing the code and testing it in Visual Studio it was pretty easy to port it into Blue Prism.

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml;
    using System.Net;
    using System.IO;
    using System.Xml.Linq;
    namespace WebService
    {
        class Program
        {
            /// <summary>
            /// Execute a Soap WebService call
            /// </summary>
            public static string Execute(string queryNo)
            {
                HttpWebRequest request = CreateWebRequest();
                XmlDocument soapEnvelopeXml = new XmlDocument();
                soapEnvelopeXml.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?>
                    <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:customNAMESPACE=""http://webservice.com"">
                    <soapenv:Header>
                        <wsse:Security xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
                            <wsse:UsernameToken>
                                <wsse:Username>USER</wsse:Username>
                                <wsse:CustomField>CODE</wsse:CustomField>
                            </wsse:UsernameToken>
                         </wsse:Security>
                      </soapenv:Header>
                      <soapenv:Body>
                         <customNAMESPACE:QueryByQueryNo>
                            <!--Optional:-->
                            <QueryByQueryNo>
                                <!--Optional:-->
                                <queryNo>" + queryNo + @"</queryNo>
                            </QueryByQueryNo>
                          </customNAMESPACE:QueryByQueryNo>
                      </soapenv:Body>
                    </soapenv:Envelope>");
    
                using (Stream stream = request.GetRequestStream())
                {
                    soapEnvelopeXml.Save(stream);
                }
    
                using (WebResponse response = request.GetResponse())
                {
                    using (StreamReader rd = new StreamReader(response.GetResponseStream()))
                    {
                        string soapResult = rd.ReadToEnd();
                        Console.WriteLine(soapResult);
                        return soapResult;
                    }
                }
            }
            /// <summary>
            /// Create a soap webrequest to [Url]
            /// </summary>
            /// <returns></returns>
            public static HttpWebRequest CreateWebRequest()
            {
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(@"https://webservice.com/webservice?wsdl");
                webRequest.Headers.Add(@"SOAP:Action");
                webRequest.ContentType = "text/xml;charset=\"utf-8\"";
                webRequest.Accept = "text/xml";
                webRequest.Method = "POST";
                return webRequest;
            }
            static void Main(string[] args)
            {
                if (args.Length == 0 || args.Length > 1)
                {
                    System.Console.WriteLine("Please provide a query no");
                    System.Console.WriteLine("Usage: WebService.exe 3523423333");
                    return;
                }
    
                string output, XMLresponse;
                try
                {
                    XMLresponse = Execute(args[0]);
                    output = "Successful query";
                    XmlDocument xml = new XmlDocument();
                    xml.LoadXml(XMLresponse);  // suppose that str string contains the XML data. You may load XML data from a file too.
                    XmlNodeList resultCodeList = xml.GetElementsByTagName("resultCode");
                    XmlNodeList resultNoList = xml.GetElementsByTagName("resultNo");
                    int i = 0;
                    var OutputTable = new DataTable();
                    OutputTable.Columns.Add("Result Code", typeof(string));
                    OutputTable.Columns.Add("Result No", typeof(string));
                    foreach (XmlNode xn in resultCodeList)
                    {
                        Console.WriteLine(resultCodeList[i].InnerText + "  " + resultNoList[i].InnerText);
                        OutputTable.Rows.Add(resultCodeList[i].InnerText, resultNoList[i].InnerText);
                        i++;
                    }
    
                }
                catch (System.Net.WebException exc)
                {
                    Console.WriteLine("HTTP POST request failed!");
                    output = "!!!HTTP POST request failed!!!";
                }
            }
        }
    }