在 SOAP 请求中使用自定义字段实现 WSSE 安全 Headers 时出现 C# 运行时错误
C# Runtime Error when implementing WSSE Security Headers with custom fields in SOAP request
我正在尝试向使用 WSSE 和 UsernameToken 进行身份验证的 Web 服务发送 SOAP 请求。示例查询如下(屏蔽机密数据):
<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>
我使用 WSE 3.0 生成了一个代理 class,问题是我收到错误:"Object reference not set to an instance of an object." 我的 C# 代码中有问题的部分如下:
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;
我通过系统地注释掉部分代码找到了有问题的部分。我对 Web 服务、C# 很不熟悉,我实际上是在 Blue Prism 中实现它。虽然这个程序可以开箱即用地使用 SOAP Web 服务,但遗憾的是它本身并不支持 SOAP headers。
SOAP 请求在 SOAP UI 中工作正常,并且在 Blue Prism 中没有编译器错误。我尝试按照手册和网络上的说明添加 headers,但没有成功。如果您能指出正确的方向,我将不胜感激。
编辑
编写后,在 Visual Studio 2017 中编译控制台应用程序时出现以下错误。据我了解,它没有 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
我认为是xml结构准时headers
当你使用
<wsse:Security
wsse 没有定义,我知道你在同一行定义了但为什么不试试把它放在文档中,像这样
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:kas="http://webservice.com" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soapenv:Header>
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>abc</wsse:Username>
<wsse:CustomField>123</wsse:CustomField>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
我决定使用不同的方法并暂时停止尝试使用代理 class,因为它存在相关问题。利用这个 link 上的答案:Client to send SOAP request and receive response 经过一些定制,我想出了自己的解决方案。
但是,我仍然想知道如何继续使用由 Visual Studio 或 WSE 3.0 定义的 wrapper classes 使其工作。编写代码并在 Visual Studio 中测试后,很容易将其移植到 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!!!";
}
}
}
}
我正在尝试向使用 WSSE 和 UsernameToken 进行身份验证的 Web 服务发送 SOAP 请求。示例查询如下(屏蔽机密数据):
<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>
我使用 WSE 3.0 生成了一个代理 class,问题是我收到错误:"Object reference not set to an instance of an object." 我的 C# 代码中有问题的部分如下:
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;
我通过系统地注释掉部分代码找到了有问题的部分。我对 Web 服务、C# 很不熟悉,我实际上是在 Blue Prism 中实现它。虽然这个程序可以开箱即用地使用 SOAP Web 服务,但遗憾的是它本身并不支持 SOAP headers。
SOAP 请求在 SOAP UI 中工作正常,并且在 Blue Prism 中没有编译器错误。我尝试按照手册和网络上的说明添加 headers,但没有成功。如果您能指出正确的方向,我将不胜感激。
编辑 编写后,在 Visual Studio 2017 中编译控制台应用程序时出现以下错误。据我了解,它没有 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
我认为是xml结构准时headers 当你使用
<wsse:Security
wsse 没有定义,我知道你在同一行定义了但为什么不试试把它放在文档中,像这样
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:kas="http://webservice.com" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soapenv:Header>
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>abc</wsse:Username>
<wsse:CustomField>123</wsse:CustomField>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
我决定使用不同的方法并暂时停止尝试使用代理 class,因为它存在相关问题。利用这个 link 上的答案:Client to send SOAP request and receive response 经过一些定制,我想出了自己的解决方案。
但是,我仍然想知道如何继续使用由 Visual Studio 或 WSE 3.0 定义的 wrapper classes 使其工作。编写代码并在 Visual Studio 中测试后,很容易将其移植到 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!!!";
}
}
}
}