Delphi SOAP Envelope and WCF - c#

I am working on a system that provides a soap interface. One of the systems that are going to use the interface is coded in Delphi 7. The web service is developed with WCF, basic http binding, SOAP 1.1.
If I use SOAP UI (JAVA), the service works properly. But Delphi seems to do special things here ;)
This is how the message looks like in SOAP UI:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.xxx.de/xxx">
<soapenv:Header/>
<soapenv:Body>
<ser:GetCustomer>
<!--Optional:-->
<ser:GetCustomerRequest> <!-- this is a data contract -->
<ser:Id>?</ser:Id>
</ser:GetCustomerRequest>
</ser:GetCustomer>
</soapenv:Body>
</soapenv:Envelope>
I am not a delphi developer , but I developed a simple test client to see what's going wrong. This what Delphi sends as a SOAP envelope.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:NS2="http://services.xxx.de/xxx">
<NS1:GetCustomer xmlns:NS1="http://services.xxx.de/xxx">
<GetCustomerRequest href="#1"/>
</NS1:GetCustomer>
<NS2:GetCustomerRequest id="1" xsi:type="NS2:GetCustomerRequest">
<Id xsi:type="xsd:int">253</Id>
</NS2:GetCustomerRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
WCF throws an error that is in German language... ;)
Es wurde das Endelement "Body" aus Namespace "http://schemas.xmlsoap.org/soap/envelope/" erwartet. Gefunden wurde "Element "NS2:GetCustomerRequest" aus Namespace "http://services.xxx.de/xxx"". Zeile 1, Position 599.
Means something like
The Body was expected. But instead the Element "NS2:GetCustomerReques" was found.
Now my questions is: Can I somehow change the way Delphi creates the envelope? Or are the ways to make WCF work with such message formats? Any help is greatly appreciated!

Contrary to what some people here seem to be implying, Delphi is not sending invalid SOAP, it is simply sending RPC/Encoded SOAP. It's very easy to recognize from all the xsi:type attributes. RPC/Encoded is not WS-I compliant but it is still valid SOAP.
WCF, by default, uses the Document/Literal/Wrapped SOAP format, which Delphi 7 can't handle at all on the server side and you have to make some adjustments on the client side.
The simplest solution is to simply tell Delphi to use the Document/Literal style. You do that by turning on soLiteralParams in the THttpRio.Converter.Options. This tells Delphi not to "unwind" the parameters as you are seeing. The "Document" aspect is something that the Delphi WSDL importer can usually figure out so you shouldn't need to worry about that.
The other solution is to tell the WCF service to use RPC/Encoded style, which you can do by adding the following attributes to the service:
[ServiceContract]
[XmlSerializerFormat(Style = OperationFormatStyle.Rpc,
Use = OperationFormatUse.Encoded)]
public interface IMyService
{
// etc.
}
The second is not recommended because, as I mentioned earlier, RPC/Encoded is not WS-I compliant, but nevertheless most SOAP toolkits do recognize it, so I list it here as a possibility.

I just did one of these, and I ended up with a series of stringreplace calls to alter my XML output to strip the in-line namespaces out and make it look like SoapUI's format. Yes it takes a lot of manual hacking to do that.
ex:
After you create the RIO, call your own BeforeExecute proc:
...
EEUPSERTRIO.OnBeforeExecute := self.RIO_BeforeExecute;
...
procedure TMyWrapper.RIO_BeforeExecute(const MethodName: string; var SOAPRequest: WideString);
{
Since Delphi isn't very good at SOAP, we need to fix the request so that the namespaces are correct.
Basically, you take what Delphi gives you and try it in SoapUI.
If yours doesn't work and SoapUI's version does, make yours look like theirs.
}
...
Now strip out the in-line Namespaces:
SOAPRequest := StringReplace(SOAPRequest,' xmlns:NS1="http://services.xxx.de/xxx"','',[rfReplaceAll,rfIgnoreCase]);
...
Lots of these.
Then you'll replace the soap header with one that contains the namespaces that you want.
SOAPRequest := StringReplace(SOAPRequest,'xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"','xmlns:ns1="http://services.xyzcorp.com/xyz/EnterpriseEmployeeService_1_0" '+'xmlns:ns1="http://schemas.xyzcorp.com/TLOIntegration_HRO_Preview/TLOIntegration_1_0" ',[]);
Then you can re-inject good ones:
ReplaceTag(SOAPRequest,'<metaData>','ns1:');
ReplaceTag(SOAPRequest,'<trackingId>','ns1:');
ReplaceTag(SOAPRequest,'<srcSystem>','ns1:');
Lastly, you can easily capture your Delphi output by re-consuming the WSDL with SoapUI and having it host a mockservice. Then point your app to it as the endpoint, and it'll capture the ouput.
Or, you can use Fiddler as a proxy, to capture requests.

Delphi and Java frameworks use different name space. One way to make if compatible is to intercept the raw xml and change all the "NS2" to whatever the deserializer expects
Cheers

Related

Finding out why this web service is failing

I am dealing with a web service from one of my government agencies for electronic documents. The WSDL can be found here: https://maullin.sii.cl/DTEWS/CrSeed.jws?WSDL
I tried calling the getSeed() method (which is the only relevant one) at http://www.soapclient.com/soaptest.html to see if it's working, and indeed it is.
I created a WCF Service Library to test this and i got the following error:
System.ServiceModel.FaultException: 'org.xml.sax.SAXParseException: Content is not allowed in prolog.'
A quick online search shows that many users have this problem trying to implement this particular Web service and they all seems to point out some windows update. Everyone points to a different one to uninstall and that's how some of them resolved this issue.
I don't believe it's a matter of a particular windows update, perhaps there is something else. So instead I tried creating the WCF Service Application and hosting the web service in IIS to check if maybe it was some debug problem.
In a console project I try to call the getSeed() method, but it ended up returning a null string instead of throwing a SAXParseException.
So whats the deal in here?. It seems pretty straight forward to me:
1. Add the service reference
2. Create a new instance of CrSeedClient class
3. Call getSeed() method.
Why I am getting all this trouble over this particular web service?
BTW, i am using Net Framework 4.7.2 / Windows 10 / Visual Studio 2017
Can anyone test it out please?
Thanks.
EDIT !: Read my own answer...
Ok so in the end this was a nightmare. It involved first disable a security patch made by
microsoft. Here the details:
https://support.microsoft.com/en-us/help/3155464/ms16-065-description-of-the-tls-ssl-protocol-information-disclosure-vu
I made it programmatically:
AppContext.SetSwitch("TestSwitch.LocalAppContext.DisableCaching", true);
AppContext.SetSwitch("Switch.System.Net.DontEnableSchSendAuxRecord", true);
That way i could get past the org.xml.sax.SAXParseException which was a response made by the WebServer. Even when i changed the raw message using a custom message enconder, it seems to be WCF or maybe even the OS was writting some bytes or modify the final SOAP message on the fly. Disabling that security patch on the fly prevented this from happening.
Now next thanks to the custom message encoder, i could see that the webservice was finally returning a valid message, but WCF didnt parse it correctly. After hours of testing, i figure it out:
Original response:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getSeedResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<getSeedReturn xsi:type="xsd:string">
<?xml version="1.0" encoding="UTF-8"?><SII:RESPUESTA xmlns:SII="http://www.sii.cl/XMLSchema"><SII:RESP_BODY><SEMILLA>013052590000</SEMILLA></SII:RESP_BODY><SII:RESP_HDR><ESTADO>00</ESTADO></SII:RESP_HDR></SII:RESPUESTA>
</getSeedReturn>
</getSeedResponse>
</soapenv:Body>
</soapenv:Envelope>
By removing the ns1 prefix everywhere (including xmlns:ns1="http://DefaultNamespace") i could finally get the correct parsing.
After fixed:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getSeedResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<getSeedReturn xsi:type="xsd:string">
<?xml version="1.0" encoding="UTF-8"?><SII:RESPUESTA xmlns:SII="http://www.sii.cl/XMLSchema"><SII:RESP_BODY><SEMILLA>013052590000</SEMILLA></SII:RESP_BODY><SII:RESP_HDR><ESTADO>00</ESTADO></SII:RESP_HDR></SII:RESPUESTA>
</getSeedReturn>
</getSeedResponse>
</soapenv:Body>
</soapenv:Envelope>
I still dont understand neither the security patch or why WCF fails to parse the message with the NS1 prefix.
If anyone dare to take a look at this i would be very happy, cause i think this solutions are a little hacky and honestly i can see why people would prefer to use java instead of WCF.
In your scenario (.net 4.7) I prefer to use this technology: "add web reference" and not "add service reference"
Here you can find the difference between using one or another technology, one is more current than the other:
Web Reference vs. Service Reference
Since your scenario is not .net core then you have a choice.

How to create a WCF Service with no structure (XML request and response)

In my most recent project, I've been given a WSDL and a sample request to build a web service with. WCF has weak contract first capability, but I did come up with something complicated that might work (I tried a number of tools, the best being WSCF.Blue). The SOAP request headers, unwrapped body, and XSD are unorthodox and I'm afraid that this integration partner will have lots of strange XML issues, so I'd rather, if possible, just have a service that looks something like this:
[ServiceContract]
public interface IMyService
{
[OperationContract]
XmlElement DoStuff(XmlElement request);
}
instead of...
[ServiceContract]
public interface IMyService
{
[OperationContract]
[XmlSerializerFormat(SupportFaults = true, Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]
ResponseType DoStuff(RequestType request);
}
A super simplified version of what this SOAP request looks like below:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<ns1:CustomHeader priority="1" txId="1" xmlns:ns1="http://namespace"/>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<UnwrappedRequest creationDateTime="2014-04-21T16:15:37Z" xmlns="http://anothernamespace.com">
<ns1:SomeDataNode xmlns:ns1="http://namespace">
<ns1:MoreData AFlag="false" Thingy="Dude">
</ns1:MoreData>
</ns1:SomeDataNode>
</UnwrappedRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The main reason I want to stick with WCF instead of just creating an HTTP handler or something is because we have to use WS-Security also, which includes a bunch of other headers and the session establishing request before the actual API call. I've tried a couple things that almost work, but they exclude the top node of the request and all of it's attributes (the node in the sample above). I also need the custom header XML. I know you can bind that into an property on the request object, but I want the request to just be a string of XML.
You would think that every framework out there should be able to easily do this and I know that WCF isn't an exception. The thing we all love about WCF is how flexible and powerful it is. The problem is that most of us don't live in WCF and integration, so we don't ever become experts, and I have a timeline to meet. Any help would be greatly appreciated!!
I know I could have done this by implementing or overriding a bunch of WCF internal interfaces and classes, but I took the easy route and built a custom HttpHandler to handle the soap call. It works perfectly (and it's SUPER fast). The only somewhat challenging piece was building in the WS-Security stuff, but even that wasn't too bad. It is clear to me that the system they were calling from was just performing a custom HTTP post anyway. It's unfortunate that Microsoft's tooling doesn't support such a large number of valid Web Service scenarios. I talked to a few experienced integration experts, and they abandoned WCF for custom solutions in many of their partner integrations. If you are in control of both sides of an integration project, then great, use WCF. If you are working with partners that have a wide variety of technologies to integrate with, be prepared to either become an expert at customizing WCF or find alternate solutions.
Feel free to send me a message if you are having this same problem and I can share my code with you. There is just too much to paste in here.

Return XML when consuming a secure web service

I am currently trying to consume a generated report as a webservice to integrate some data into our system. Because the service itself is generated, the response can change frequently as things are added to it. While the endpoint and response may change, the request body will always be the same (taken from soapui):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<Execute_Report>
</Execute_Report>
</soapenv:Body>
</soapenv:Envelope>
I'm trying to figure out a way to make the above request for any endpoint (different reports) and allow for changes in the response. Ideally, I want to just return the raw XML of the response as I can allow for change easier with how I handle the XML if I'm not being tied to a data contract.
It is also worth noting that the service uses WS-Security and a Username/Password is passed as part of the request.
I've used WCF and the files generated from svcutil work great when I don't expect the service to change frequently. However because these webservices are generated change is expected, and if I can get away from it, I don't want to be at the mercy of re-generating a new file with svcutil whenever things change, or have to generate a file (and maintain) for all the different generated webservices.
At the end of the day the question is: How do I consume a webservice and return the raw XML while still being able to apply WS-Security to the request?
I kept searching around and found this answer:
.NET client authentication and SOAP credential headers for a CXF web service
This allowed me to do what I was after.

WCF Request Formatting

I am converting a legacy web service to a WCF service. This service currently is being used by front end application. But as of now the front end is not going to consume the new WCF service so instead we plan on rerouting the request to our new service. The catch here is that the new WCF service should be able handle the old input request and should be able to send back the response in the exact same format.
When I generate a wsdl and add it in the soapUI project. The Body is getting wrapped inside a tag with the method name ("UserVerification" is the Operation contract name), is there any way to handle this with out using message contracts(I am using legacy types for input parameters so cant change them)
Right now it is coming like this:
<soapenv:Body>
<wes:UserVerification>
<!--Optional:-->
<wes:userVerificationRequest wes:Direction="Request" >
</wes:userVerificationRequest>
</wes:UserVerificatio>
</soapenv:Body>
I want it to be like this
<soapenv:Body>
<wes:UserVerificationRequest wes:Direction="Request" >
</wes:UserVerificationRequest>
</soapenv:Body>
WCF by default uses the 'Wrapped' message style. If you want to be able to control how messages are serialized, you can define explicit messages by decorating with the MessageContractAttribute. With explicit message contracts, you can set the IsWrapped property to false.
In your case I think that EchoRequest and EchoResponse shouldn't be DataContracts at all, but rather MessageContracts. They look a lot like MessageContracts to me.
Using Message Contracts

Soap web service and android client using ksoap protocal mismatch

I have and android client, that uses ksoap to communicate with a wsdl web service written in c# asp.net. I have a problem with matching the argument types between the web service and the client.
The web server expects to this kind of request (auto generated):
<?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>
<GetDetails xmlns="http://host.org/">
<event_id>int</event_id>
</GetDetails>
</soap:Body>
</soap:Envelope>
the client sends requests using ksoap, and they look like this:
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header />
<v:Body>
<n0:GetDetails id="o0" c:root="1" xmlns:n0="http://tempuri.org">
<event_id i:type="d:int">1</event_id>
</n0:GetDetails>
</v:Body>
</v:Envelope>
For some reason the WS parses the following' client's request as 0 (I guess because of the additional type attributes - i:type="d:int") when the request is assembled manually to look like the first option, it works correctly.
How can i make the web service read the ksoap format correctly or how can i change it's expected format to look like ksoap request. (the web service soap protocol is auto generated).
Well the solution has 2 parts:
1. regarding removing the attribute types, I found the answer here:
using addMapping without the "i:type=" attribute in ksoap2 for android
I neede to set:
envelope.implicitTypes = true;
The name space has to end with a backspace, and i missed it.

Categories

Resources