Deserialize HttpResponseMessage of Soap:Envelope to C# Object - c#

I'm calling Soap method and receiving HttpResponseMessage as below :
HttpResponseMessage createResponse = await ExecuteMyEndPoint(endpoint, requestXMLstring, client);
And reading that as a String Response:
string responseMessage = await createResponse.Content.ReadAsStringAsync();
This is the Sample Response :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns1:createWorkflowResponse xmlns:ns1="http://www.somedomain.com/xml/workflow">
<WorkflowExternalReference xmlns:ns3="http://www.somedomain.com/xml/integration">123456789</WorkflowExternalReference>
</ns1:createWorkflowResponse>
</soap:Body>
</soap:Envelope>
I want to convert this response to C# object. How to do that?

Related

How to pass XML input to API call using C#

I have below XML input. I need to call API and pass this as input but values will change dynamically. How can I build this input structure?
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Test xmlns="http://tempuri.org/">
<acc>test</acc>
<pass>abc</pass>
<xmlInvData>
<![CDATA[
<MyData>
<name>test</name>
<number>900</number>
</MyData>
]]>
</xmlInvData>
<username>test</username>
<password>123</password>
</Test>
</soap:Body>
</soap:Envelope>
I have MyData Class in C# which can be useful to setup name and number values.
But how can I form a complete structure and pass to Api call? soap:Envelop and soap body?
HttpClient httpClient = new HttpClient();
string requestUri = "https://testurl";
var byteArray = Encoding.ASCII.GetBytes("username:password");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
HttpResponseMessage response = await httpClient.PostAsync(requestUri, httpContent);
I need to understand how to form httpContent as above my input json.
Assuming you've built the data structure and serialized it to a string called xml:
var httpContent = new StringContent(xml, Encoding.UTF8, "application/xml");

How to generate UsernameToken for WS-Security?

I have some web service (wrote in Java) and I need create client for it in .NET. The WebService has WS-Security and need PasswordDigest.
First of all, I've tested it in SoapUI and it's works for:
POST http://192.168.100.101:8181/services/ws/SomeService HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/soap+xml;charset=UTF-8;action="someMethod"
Content-Length: 971
Host: 192.168.100.101:8181
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
<soap:Envelope xmlns:not="http://existing-domain.com/op/integration/services" xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-2C1E2DE2B61EBB94E115572099598331">
<wsse:Username>SOME_LOGIN</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">rcSb6Hd8btcI9g6JvO7dGdiTBTI=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">PCoVwJm9oEXtusx6gkMb7w==</wsse:Nonce>
<wsu:Created>2019-05-07T06:19:19.824Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<not:someMethod/>
</soap:Body>
</soap:Envelope>
In the next step I've prepared simple client in .NET and check with Wireshark what it sends:
POST /services/ws/SomeService HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/soap+xml;charset=UTF-8;action="someMethod"
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Host: 192.168.100.101:8181
Content-Length: 1124
Expect: 100-continue
Connection: Keep-Alive
<soap:Envelope xmlns:not="http://existing-domain.com/op/integration/services" xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-85707168-b5c6-47dc-93e9-45afa466fa2a" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Username>SOME_LOGIN</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">g112a9eHPR1hXD4UH+Lh3o8JV/o=</wsse:Password>
<wsse:Nonce>EiUbo2DbXMGhM26fT+ZkJQ==</wsse:Nonce>
<wsu:Created>2019-05-07T06:27:59Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<not:someMethod />
</soap:Body>
</soap:Envelope>
Unfortunatelly I allways got 500 status code and response:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<soap:Fault>
<soap:Code>
<soap:Value>soap:Sender</soap:Value>
<soap:Subcode>
<soap:Value xmlns:ns1="http://ws.apache.org/wss4j">ns1:SecurityError</soap:Value>
</soap:Subcode>
</soap:Code>
<soap:Reason>
<soap:Text xml:lang="en">A security error was encountered when verifying the message</soap:Text>
</soap:Reason>
</soap:Fault>
</soap:Body>
</soap:Envelope>
I'm pretty sure it's appear because of authorization problem (I had same message in SoapUI if provided wrong credentials).
The section in my client is created with:
UsernameToken t = new UsernameToken("SOME_LOGIN", "SOME_PASSWORD", PasswordOption.SendHashed);
string usernameTokenSection = t.GetXml(new XmlDocument()).OuterXml.ToString();
There is a few articels how to create PasswordDigits (Base64( SHA1(password + nonce + created) )), Nonce (Base64(RandomString)) or Created date but I can't find what is wsu:Id="UsernameToken-2C1E2DE2B61EBB94E115572099598331" and how to create it. Above code where I get xml from UsernameToken returns complete section so I decided to use it, but I noticed that it append wsu:Id="SecurityToken-85707168-b5c6-47dc-93e9-45afa466fa2a" (instead of wsu:Id="UsernameToken-2C1E2DE2B61EBB94E115572099598331"). I can change the name and remove - characters but it's nothing changed and I still get 500 Internal Server Error with message "A security error was encountered when verifying the message".
So, my question is how to generate UsernameToken section with proper wsu:Id="UsernameToken-XXXXXXXXXXXXXXX"data? What is it - just random string or some hash created based on username and password?
Got it! I noticed that SOAP sent from .NET client doesn't have attribute EncodingType in Nonce node. After little modification in code:
UsernameToken t = new UsernameToken("SOME_LOGIN", "SOME_PASSWORD", PasswordOption.SendHashed);
string usernameTokenSection = t.GetXml(new XmlDocument()).OuterXml.ToString().Replace("<wsse:Nonce", "<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\"");
Everything works!
Whole code for .NET client:
public static void Execute()
{
UsernameToken usernameTokenSection = new UsernameToken("SOME_LOGIN", "SOME_PASSWORD", PasswordOption.SendHashed);
HttpWebRequest request = CreateWebRequest();
XmlDocument soapEnvelopeXml = new XmlDocument();
soapEnvelopeXml.LoadXml(#"<soap:Envelope xmlns:not=""http://existing-domain.com/op/integration/services"" xmlns:soap=""http://www.w3.org/2003/05/soap-envelope"">" +
"<soap:Header>" +
#"<wsse:Security xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"" xmlns:wsu=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"">"+
usernameTokenSection.GetXml(new XmlDocument()).OuterXml.ToString().Replace("<wsse:Nonce", "<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\"") +
"</wsse:Security>" +
"</soap:Header>"+
"<soap:Body>"+
"<not:someMethod/>"+
"</soap:Body>"+
"</soap: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);
}
}
}
public static HttpWebRequest CreateWebRequest()
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(#"http://192.168.100.101:8181/services/ws/SomeService");
webRequest.Method = "POST";
webRequest.Headers.Add("Accept-Encoding:gzip,deflate");
webRequest.ContentType = "application/soap+xml;charset=UTF-8;action=\"someMethod\"";
//NOTE: below it's not necessary
//webRequest.Host = "192.168.100.101:8181";
//webRequest.KeepAlive = true;
//webRequest.UserAgent = "Apache-HttpClient/4.1.1 (java 1.5)";
return webRequest;
}
static void Main(string[] args)
{
try
{
Execute();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.ReadLine();
}
}
PS. For using UsernameToken class you have to using Microsoft.Web.Services2 reference, you can add it by Nuget from here: Microsoft.Web.Services2
PPS. I know that there is simpler way by Add Service Reference... but it require additional classes and modifications for creating Security node in Header, so I decided to send raw xml and manipulate it directly.

Connecting To Web Services through Xamarin

I am new to Xamarin and am trying to connect to my client web services whose address is http://smartasset-utw.malaysiaairports.com.my:5010/service1.asmx?op=GetUserLogin. This is used to authenticate logins. The code that I have used is below.
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
httpClient.DefaultRequestHeaders.Add("SOAPAction", "http://smartasset-utw.malaysiaairports.com.my:5010/service1.asmx?WSDL");
string wUser = "XXXXX";
string wPassword = "xxxxxxxx";
string soapstr = string.Format(#"<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
<soap:Body>
<GetUserLogin xmlns=""http://tempuri.org/"">
<userName>{0}</userName>
<password>{1}</password>
</GetUserLogin>
</soap:Body>
</soap:Envelope>", wUser, wPassword);
var response = httpClient.PostAsync("http://smartasset-utw.malaysiaairports.com.my:5010/service1.asmx?WSDL", new StringContent(soapstr, Encoding.UTF8, "text/xml")).Result;
var content = response.Content.ReadAsStringAsync().Result;
Unfortunately, the response that I get is:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><faultcode>soap:Client</faultcode><faultstring>Server did not recognize the value of HTTP Header SOAPAction: http://smartasset-utw.malaysiaairports.com.my:5010/service1.asmx.</faultstring><detail /></soap:Fault></soap:Body></soap:Envelope>
Any help would be appreciated,
Thanks

Consume Web Service with soap:mustUnderstand attribute

This is my sample Code for Web service. I'm new to SOAP application if someone can spot any problem here it's much appreciated. This error only happened if mustUnderstand attribute ="1"
[WebService(Namespace = "http://www.xxxx.co.uk/Integration/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class ADNHeaderContact : System.Web.Services.WebService
{
public MyHeader myHeader;
[WebMethod]
[SoapHeader("myHeader")]
public string HelloWorld()
{
XmlDocument xmlSoapRequest = new XmlDocument();
using (Stream receiveStream = HttpContext.Current.Request.InputStream)
{
receiveStream.Position = 0;
using (StreamReader readStream =
new StreamReader(receiveStream, Encoding.UTF8))
{
xmlSoapRequest.Load(readStream);
}
}
using (XmlBreaker readxml = new XmlBreaker())
{
using (ReponseSaveApplications respose = new ReponseSaveApplications())
{
return ("Hello");
}
};
}
}
My Postman post request
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<MyHeader soap:mustUnderstand="true" xmlns="http:www.xxxx.co.uk/Integration/">
<MyValue>string</MyValue>
</MyHeader>
</soap:Header
<soap:Body>
<HelloWorld xmlns="http://www.xxxx.co.uk/Integration/" />
</soap:Body>
</soap:Envelope>
My Postman post response
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultcode>soap:MustUnderstand</faultcode>
<faultstring>System.Web.Services.Protocols.SoapHeaderException: SOAP header MyHeader was not understood.
at System.Web.Services.Protocols.SoapHeaderHandling.SetHeaderMembers(SoapHeaderCollection headers, Object target, SoapHeaderMapping[] mappings, SoapHeaderDirection direction, Boolean client)
at System.Web.Services.Protocols.SoapServerProtocol.CreateServerInstance()
at System.Web.Services.Protocols.WebServiceHandler.Invoke()
at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
SoapHeader.MustUnderstand Property
When an XML Web service client adds a SOAP header to an XML Web
service method call with the MustUnderstand property set to true, the
XML Web service method must set the DidUnderstand property to true;
otherwise, a SoapHeaderException is thrown back to the XML Web service
client by ASP.NET.

How can I add a SOAP authentication header with HTTPRequestMessage?

Here is what the header is supposed to look like
<soap:Header>
<AuthenticationHeader>
<UserName>string</UserName>
<Password>string</Password>
</AuthenticationHeader>
</soap:Header>
Here is what I've tried:
string username = "TheUserName";
string password = "ThePassword";
HttpRequestMessage requestMessage = new HttpRequestMessage(method, uri);
requestMessage.Headers.Add("UserName", username);
requestMessage.Headers.Add("Password", password);
Maybe I have to somehow set the authorization header?
requestMessage.Headers.Authorization = ??
I feel like somehow I have to "build" that AuthenticationHeader element but I'm not sure how to do that. Any suggestions?
Edit: Full SOAP Envelope
?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<AuthenticationHeader xmlns="http://www.test.com/testing/Security">
<UserName>string</UserName>
<Password>string</Password>
</AuthenticationHeader>
</soap:Header>
<soap:Body>
<GetMeSomething xmlns="http://www.test.com/testing/WorkFileCatalog">
<Param1>string</Param1>
<Param2>string</Param2>
<XMLRetMess>string</XMLRetMess>
</GetMeSomething>
</soap:Body>
</soap:Envelope>
Given the provided OP, the following Unit Test was done as a proof of concept of how you can populate the header message header and create a request.
[TestClass]
public class SOAP_UnitTests {
private HttpMethod method;
private string uri;
private string action;
[TestMethod]
public void _Add_SOAP_Auth_Header_Details_With_HttpRequestMessage() {
string username = "TheUserName";
string password = "ThePassword";
var xml = ConstructSoapEnvelope();
var doc = XDocument.Parse(xml);
var authHeader = doc.Descendants("{http://www.test.com/testing/Security}AuthenticationHeader").FirstOrDefault();
if (authHeader != null) {
authHeader.Element(authHeader.GetDefaultNamespace() + "UserName").Value = username;
authHeader.Element(authHeader.GetDefaultNamespace() + "Password").Value = password;
}
string envelope = doc.ToString();
var request = CreateRequest(method, uri, action, doc);
request.Content = new StringContent(envelope, Encoding.UTF8, "text/xml");
//request is now ready to be sent via HttpClient
//client.SendAsync(request);
}
private static HttpRequestMessage CreateRequest(HttpMethod method, string url, string action, XDocument soapEnvelopeXml) {
var request = new HttpRequestMessage(method: method, requestUri: url);
request.Headers.Add("SOAPAction", action);
request.Headers.Add("ContentType", "text/xml;charset=\"utf-8\"");
request.Headers.Add("Accept", "text/xml");
request.Content = new StringContent(soapEnvelopeXml.ToString(), Encoding.UTF8, "text/xml"); ;
return request;
}
private string ConstructSoapEnvelope() {
var message = #"<?xml version='1.0' encoding='utf-8'?>
<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>
<soap:Header>
<AuthenticationHeader xmlns='http://www.test.com/testing/Security'>
<UserName>string</UserName>
<Password>string</Password>
</AuthenticationHeader>
</soap:Header>
<soap:Body>
<GetMeSomething xmlns='http://www.test.com/testing/WorkFileCatalog'>
<Param1>string</Param1>
<Param2>string</Param2>
<XMLRetMess>string</XMLRetMess>
</GetMeSomething>
</soap:Body>
</soap:Envelope>
";
return message;
}
}
If you are using HttpClient to POST a request, then you should build the full XML request.
In other words, you would build the exact Soap XML including all the elements
string requestXml = your actual full soap xml
string result = HttpClient.Post ( your actual xml )

Categories

Resources