Send SOAP document to API endpoint - c#

I am looking to send a SOAP document to an API.
The method I want to use is to create a c# object that reflects the soap document and then convert this object to a string before using this
string as content in my HTTPContent object that I send using a HTTPClient PostAsync Method.
The structure of the SOAP document is as follows :-
<SOAPENV:Envelope
xmlns:SOAPENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:SOAPENC="http://www.w3.org/2003/05/soap-encoding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAPENV:Body>
<StateRequest>
<SourceSystem>SOURCESYSTEMHERE</SourceSystem>
<SourcePassword>SOURCEPASSWORDHERE</SourcePassword>
<SourceJobList>
<SourceJobID>SOURCEJOBIDHERE</SourceJobID>
</SourceJobList>
</StateRequest>
</SOAPENV:Body>
</SOAPENV:Envelope>
I want to do something like the Psuedo code below:-
HttpContent content = new StringContent(object.ToString(), Encoding.UTF8, "text/xml");
content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
var response = _client.PostAsync(new Uri("http://targeturl.com "), content).Result;
What I want to know is
What c# object and attributes do I need to create to map this document structure? I need to create a C# object as I will be using data sent from
another source to update the SourceSystem,SourcePassword and SourceJobID properties.
What serialization/configuration do i need as part of the above method as I'm sure there is more required than just doing .ToString() on object.
Regards
Macca

Related

Implement SOAP 1.2 service in asp.net core

I'm trying to implement OCPP 1.5 under ASP.Net Core 2.2. The standard makes use of SOAP 1.2. The issue comes from poor interpretation of the MessageHeader attribute. Properties with MessageHeader should be in header, but they are not.
Source code: https://github.com/delianenchev/OcppDemo
I use SoapCore under ASP.Net Core. My initialization looks like this:
var transportBinding = new HttpTransportBindingElement();
var textEncodingBinding = new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressingAugust2004, System.Text.Encoding.UTF8);
var customBinding = new CustomBinding(transportBinding, textEncodingBinding);
app.UseSoapEndpoint<ISampleService>("/SOAP/service.wsdl", customBinding, SoapSerializer.DataContractSerializer);
My demo model with the MessageHeader and MessageBodyMember attributes.
[MessageContract]
public class MessageModelRequest
{
[MessageHeader]
public string Id { get; set; }
[MessageBodyMember]
public string Name { get; set; }
[MessageBodyMember]
public string Email { get; set; }
}
I test API with SoapUI.
This is my API under ASP.NET core with SoapCore.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:mod="http://schemas.datacontract.org/2004/07/Models">
<soap:Header/>
<soap:Body>
<tem:TestMessageModel>
<!--Optional:-->
<tem:inputModel>
<!--Optional:-->
<mod:Email>?</mod:Email>
<!--Optional:-->
<mod:Id>1</mod:Id>
<!--Optional:-->
<mod:Name>?</mod:Name>
</tem:inputModel>
</tem:TestMessageModel>
</soap:Body>
</soap:Envelope>
Correct API from WCF project for IIS.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header>
<tem:Id>34</tem:Id>
</soapenv:Header>
<soapenv:Body>
<tem:MessageModelRequest>
<!--Optional:-->
<tem:Email>3</tem:Email>
<!--Optional:-->
<tem:Name>4</tem:Name>
</tem:MessageModelRequest>
</soapenv:Body>
</soapenv:Envelope>
Well, judging by the source code of SoapCore it seems to support message headers for reading the SOAP message as it uses MessageEncoder for that purpose which knows exactly how to read a SOAP message, but when it comes to serializing a response in your case it uses a native DataContractSerializer for writing the body that ignores any message contract attributes you have on your class and furthermore it doesn't have any part for writing header, just the message body.
So I guess you need to implement the header support in response messages by yourself.
First of all, add IgnoreMemberAttribute (or XmlIgnoreAttribute if you switch to SoapSerializer.XmlSerializer) on the properties you intend to add to your response message header so that data contract serializer doesn't add them to the body of the message.
Finally, you will need to locate the properties decorated with MessageHeader attribute manually and add them to your header. Luckily SoapCore has multiple options for doing that as suggested here.
As alternative if you plan to include the source of SoapCore in your solution, you could easily achieve the goal somewhere along these lines. It's easy to do so because at this place you have the full control of the message and the response you got from your service method. With the aid of reflection, you can easily find the properties of responseObject which need to be moved to the header and just forward them to responseMessage.Headers.
I know it's a bit nasty, but well... this is the price of using SOAP in .NET Core.
In .NET 6, you can, for example, do the following to switch to SOAP 1.2 (a.k.a. soap12):
// In Program.cs ...
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// ...
app.UseSoapEndpoint<IMyServiceContract>("/MyService.asmx", new SoapEncoderOptions()
{
// Use SOAP version 1.2 (aka Soap12)
MessageVersion = MessageVersion.Soap12WSAddressingAugust2004
// - OR -
MessageVersion = MessageVersion.Soap12WSAddressing10
}, caseInsensitivePath: true);
That will result in:
<wsdl:definitions xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" ...>

How to consume SOAP service with custom header

I need to consume a SOAP service from a vendor. I had created a proxy service with the WSDL in visual studio, instantiated the client class, called the action method, and got the response. Everything works fine until the vendor asks for an access token in the soap envelop header. I am able to get the access token from them on another service call but how do I add it to the soap request header?
Here is the structure of the header from the vendor:
<SOAP:Header>
<SOAP-SEC:Security SOAP:mustUnderstand="1">
<wsse:SecuredKey ValueType="..." EncodingType="wsse:Base64Binary">
{ACCESS TOKEN}
</wsse:SecuredKey>
</SOAP-Sec:Security>
</SOAP:Header>
<SOAP:Body/>
</SOAP:Envelop>
By far the easiest way to do this is by using a string replace in a template. Store the message as a resource in your project (e.g. in Resources.resx or save as file and set set build action to embedded resource). The template looks like this:
<SOAP:Header>
<SOAP-SEC:Security SOAP:mustUnderstand="1">
<wsse:SecuredKey ValueType="..." EncodingType="wsse:Base64Binary">
{ACCESS TOKEN}
</wsse:SecuredKey>
</SOAP-Sec:Security>
</SOAP:Header>
<SOAP:Body/>
</SOAP:Envelop>
Load the template as string from your resource, then call the webservice to obtain an access token and do and replace {ACCESS TOKEN} with the actual access token. You can now send the soap message using e.g. System.Net.Http.HttpClient or System.Net.WebClient.
Example using WebClient
using (var client = new WebClient())
{
var result = client.UploadString("http://your.target/endpoint", yourXDocument.ToString(SaveOptions.DisableFormatting));
return XDocument.Parse(result);
}
The SaveOptions.DisableFormatting doesn't try to pretty print the XDocument which can be important when using a signed xml document with WS-Security. Not sure if that applies in your case.

Converting SOAP XMLString to C# object

[Question Modified] I am completely new to XML and am calling a web service(WCF). The request is expected to be in the below format:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:CreateLead>
<!--Optional:-->
<tem:xDoc>
<lead>
<![CDATA[<CreateLead>
<FIRSTNAME>John</FIRSTNAME>
<LASTNAME>Doe</LASTNAME>
<MOBILE>9999999999</MOBILE>
<EMAIL>john#doe.com</EMAIL>
<SOURCE>Website</SOURCE>
<SUBSOURCE>Blog</SUBSOURCE>
<WEBREMARKS></WEBREMARKS>
<TIMETOCALL>26/02/2018 18:40:15 PM</TIMETOCALL>
<FAILEDSTEP>Step 1</FAILEDSTEP>
<FOILIONO>123</FOILIONO>
<SCHEMECODE>123</SCHEMECODE>
<AMOUNT>100</AMOUNT>
<REGEVENT></REGEVENT>
<FEEDBACK></FEEDBACK>
</CreateLead>
]]>
</lead>
</tem:xDoc>
</tem:CreateLead>
</soapenv:Body>
</soapenv:Envelope>
I have created the proxy class and the below is the signature of the method to be called:
public System.Xml.XmlElement CreateLead(System.Xml.XmlElement xDoc)
{
return base.Channel.CreateLead(xDoc);
}
I am passing the complete SOAP as a string literal and converting it into an XMLElement and am getting a proper response. I wish to pass it as a C# object. Is there a way to create a class according to my above SOAP request?
I tried copying the XML and using Visual Studio's paste special to Paste XML as class but it is somehow not creating an exact replica of the above mentioned SOAP XML.
I need it to be completely similar to the above SOAP format.
Please help.

How Do I Change XML Prefix For WCF Client Request c#?

Due to requirements from a web service that I have no control over, I need to change my ns prefixes from the defaults of "s" and "h". The service provider has provided a .wsdl file for my service reference, however, they do not appear to have an online wsdl for metadata exchange. I have emailed them and they confirmed that the prefixes have to be specific values (right or wrong on their part, I have to make this work).
Can someone please tell me the easiest way to change these values? Is there something I can modify in Reference.cs? Do I have to implement some sort of message formatting logic into my requests to change the values just prior to sending out the request?
Every element in the XML has to have a prefix of a specific value (2 values total), or the WS will not accept the request and simply returns HTML in the response. When I copy the request captured in Fiddler, over to SOAP UI and update the prefixes to what they require, the request works fine when executed from Soap UI. I'm simply not finding any simple solutions to this seemingly simple problem for c# VS 2017 development platform.
Also worth noting, when I load the .wsdl file into Soap UI, Soap UI generates all of the ws operations and requests with the correct, desired prefixes that I need in my c# app. How does Soap UI know the exact prefixes to generate in the requests?
I need to modify this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:FooListHeader xmlns:h="http://foo.foo.com/Hello" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<h:FooList>
<h:FooStatement>
<h:Title>Foo</h:Title>
<h:Value>123</h:Value>
</h:FooStatement>
</h:FooList>
</h:FooListHeader>
</s:Header>
<s:Body>
<GetFooRequestType xmlns="http://foo.foo.com/Hello">
<MessageRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ConFooRequest/>
</MessageRequest>
</GetFooRequestType>
</s:Body>
</s:Envelope>
To something like this:
<soapenv:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:foo="http://foo.foo.com/Hello">
<soapenv:Header>
<foo:FooListHeader xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<foo:FooList>
<foo:FooStatement>
<foo:Title>Foo</foo:Title>
<foo:Value>123</foo:Value>
</foo:FooStatement>
</foo:FooList>
</foo:FooListHeader>
</soapenv:Header>
<soapenv:Body>
<foo:GetFooRequestType xmlns="http://foo.foo.com/Hello">
<foo:MessageRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<foo:ConFooRequest/>
</foo:MessageRequest>
</foo:GetFooRequestType>
</soapenv:Body>
</soapenv:Envelope>
Example c# code:
public GetYaddaYaddaResponseType CheckConnection()
{
Yadda client = new Yadda();
var msgRequest = new GetYaddaYaddaResponseType { ConnectionConfirmationRequest = new ConfirmConnectRequestType() };
List<Statement> statementList = new List<Statement>
{
new Statement { Name = "Yo", Value = "123" },
new Statement { Name = "Hey", Value = "123" },
};
var header = new StatementHeader
{
StatementList = statementList.ToArray()
};
var response = client.GetYaddaYaddaMessage(ref header, msgRequest);
return response;
}

Passing XML document as an parameter to Web services using C#

I have to sent the XML document as an parameter to request an WebRequest using the Post method and get response. Web service implements the following method:
public string Register(XmlDocument register){...}
I'm trying doing like this , but I can't get response and I'm not sure that my code is working =(
HttpWebRequest request = HttpWebRequest.Create("http://ws2.sti.gov.kg/TRKService/PatentService.asmx/Register") as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
Encoding ex = Encoding.GetEncoding("iso-8859-1");
XmlDocument doc = new XmlDocument();
doc.LoadXml("<foo><bar>baz</bar></foo>");
string rawXml = doc.OuterXml;
string requestText = string.Format("register={0}", HttpUtility.UrlEncode(rawXml, ex));
Stream requestStream = request.GetRequestStream();
StreamWriter requestWriter = new StreamWriter(requestStream, ex);
requestWriter.Write(requestText);
requestWriter.Close();
Maybe someone has a working example?
403 Error
If your getting a 403 when trying to import the web service this may not be your fault. Try
looking at the wsdl file in your web browser. If you still get the 403 error then their is no use coding any further because you don't have permission to use that service.
Code Syntax
Also, in your code I it appears your not reading back the response anywhere. Your last statement writes the XML to the stream but your not reading back the response anywhere.
requestWriter.Write(requestText);
requestWriter.Close();
SOAP
If the web service your are communicating with is SOAP based then your XML payload needs to conform to the SOAP standard. Your sample code above uses very basic XML, probably because it's just an example, but for it to work you will need requests with a format along the lines of
<?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>
<GetWeather xmlns="http://www.webserviceX.NET">
<CityName>string</CityName>
<CountryName>string</CountryName>
</GetWeather>
</soap:Body>
</soap:Envelope>
Not
<foo><bar>baz</bar></foo>
Again, you've obviously only used foo for an example but this could also be the source of your problem so inspect the actual XML payload you are sending.

Categories