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 has intention to consume the new WCF service so instead we plan on rerouting the request to our new service from IIS. 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.
I didnt have any trouble handling the input request but I am facing issues trying to return message in old format. My current service is returning SoapResponse in below format (I am nout using any message contracts/DataContracts as I am using the same return type&Input parameter used in the Old legacy code):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<UserCheckResponse xmlns="Abc.SomeNamespace">
<UserCheckResult a:Direction="Response" a:Purpose="UserCheck" xmlns:a="Abc.SomeNamespace">
<a:KV a:Key="TraceID" a:Value="546546565" />
<a:KV a:Key="Response" a:Value="78954" />
<a:KV a:Key="UserVerified" a:Value="N" />
<a:KV a:Key="TryAgain" a:Value="Y" />
<a:KV a:Key="DataSource" a:Value="NA" />
</UserCheckResult>
</UserCheckResponse>
I Want it to look like below (I dont want the UserCheckResult node in the response instead i want the key value pairs directly under UserCheckResponse node):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<UserCheckResponse a:Direction="Response" a:Purpose="UserCheck" xmlns:a="Abc.SomeNamespace">
<a:KV a:Key="TraceID" a:Value="546546565" />
<a:KV a:Key="Response" a:Value="78954" />
<a:KV a:Key="UserVerified" a:Value="N" />
<a:KV a:Key="TryAgain" a:Value="Y" />
<a:KV a:Key="DataSource" a:Value="NA" />
</UserCheckResponse>
I would say using
XMLSerialize instead of
DataContractSerializer(default for WCF).
Add "[System.ServiceModel.XmlSerializerFormatAttribute()]" for your operations.Then use XmlElementAttribute for you response node.
When I'm not busy, I will give some sample. Because I'm current working on the taking a legacy web service call and use the legacy request to call WCF service the return legacy response.
Related
I've boiled my problem down to the <Action s:mustUnderstand="1" bit below.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"></Action>
<ActivityId CorrelationId="ec9c7c4e-2e7e-4310-8ad1-99e453b29560" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">45abb8ea-8d9f-4fc2-aeb7-580884cae02e</ActivityId>
</s:Header>
<s:Body>
[redacted]
</s:Body>
</s:Envelope>
When mustUnderstand=1 is there the service barfs, and returns a web server 500 error page, not xml; with it mustUnderstand=0 or the entire <Action removed it works like a dream.
I've tried both standard basicHttpBinding as well as the following customBinding trying to simplify the messages from here:
<binding name="httpSoap12">
<textMessageEncoding messageVersion="Soap12" />
<httpTransport />
</binding>
I've seen lots of people struggling with this particular issue; but nobody seems to have a solution that I can find.
Removal of the Action bit wholesale, or setting mustUnderstand="0" both allow the service to process my incoming message. I'd welcome a solution to do either of those, or an alternative I have yet to come up with. I should specify I've done that removal/modification manually. I cannot find a way to do it from my code.
We're currently re-developing a legacy project that makes use of a SOAP web service from Lumesse, called over HTTPS.
http://developer.lumesse.com/Getting_Started
This is being consumed in a ASP.NET application; the original developer (who has long since left) never took advantage of using the provided WSDL, preferring to manually construct the request and parse the response. Whilst this is utter madness, this is what Lumesse's documentation actually recommends when being consumed from .NET, as their service uses obsolete WSSE plain text security.
Whilst we would not usually go against the grain, we'd much prefer to use the in-built support for consuming SOAP web services as opposed to rolling our own solution as the previous developer has.
We've had a few issues already, such as the failure to generate temporary classes that we have hacked around.
Unfortunately, we are now stuck when it comes to sending a successful SOAP request.
The exception that is thrown when we make a request is:
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
Digging deeper, the actual response is as follows:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header />
<env:Body>
<env:Fault xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">wsse:FailedCheck</faultcode>
<faultstring>Expired message.</faultstring>
</env:Fault>
</env:Body>
</env:Envelope>
Which appears to be that 'the signature or decryption was invalid'. Using the other developer's hobbled together solution, with the same credentials works as expected.
Why would it be failing here and what can we do to fix it?
Is it possible without resorting to rolling our own request and response (or even recommended?) service?
http://schemas.xmlsoap.org/specs/ws-security/ws-security.htm
The WSDL we have used, supplied from Lumesse:
https://api3.lumesse-talenthub.com/CareerPortal/SOAP/FoAdvert?WSDL
The endpoint we are hitting:
https://api3.lumesse-talenthub.com/CareerPortal/SOAP/FoAdvert?api_key=xxx
Here is our code so far, heavily based off With C#, WCF SOAP consumer that uses WSSE plain text authentication? - which looks like the same issue.
Correct way communicate WSSE Usernametoken for SOAP webservice has the same issue, but the answer has us storing the binding details in the web.config.
using (LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient client = new LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient())
{
client.ClientCredentials.UserName.UserName = "xxxx";
client.ClientCredentials.UserName.Password = "xxxx";
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
client.Endpoint.Binding = binding;
var response = client.getAdvertisements(new LumesseSoapTest.FoAdvert.getAdvertisements());
}
Example request Lumesse expects, taken from the part of the previous developers home-rolled solution - works as expected:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.mrted.com/">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" 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-11">
<wsse:Username>xxxx</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
xxxx
</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">5Xhsv3Yp2l1xGpL3pNYy6A==
</wsse:Nonce>
<wsu:Created>2012-06-22T09:07:26.631Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<getAdvertisements xmlns="http://ws.mrted.com/">
<firstResult>0</firstResult>
<maxResults>0</maxResults>
</getAdvertisements>
</soapenv:Body>
</soapenv:Envelope>
Example request we are currently sending:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<ActivityId CorrelationId="d309ce44-ed91-4314-87ee-e3abee4f531e" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">dd9a8c26-e673-464d-87e4-5cb8b76989c3</ActivityId>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2014-09-30T16:15:47.426Z</u:Created>
<u:Expires>2014-09-30T16:20:47.426Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-c3275c63-6d98-4ae3-a7a7-afe314d23d6c-3">
<o:Username>xxxx</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxxx</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<getAdvertisements xmlns="http://ws.mrted.com/">
<firstResult>0</firstResult>
<maxResults>0</maxResults>
</getAdvertisements>
</s:Body>
</s:Envelope>
Any help will be greatly appreciated.
Update
Right, using this hacked together XML, I can get a response from the API.
I just don't have a clue how to generate this from our request. Again any help will be greatly appreciated.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken u:Id="UsernameToken-11">
<o:Username>xxx</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxx
</o:Password>
<o:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
5Xhsv3Yp2l1xGpL3pNYy6A==
</o:Nonce>
<o:Created>2012-06-22T09:07:26.631Z</o:Created>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<getAdvertisements xmlns="http://ws.mrted.com/"><firstResult>0</firstResult><maxResults>10</maxResults>
</getAdvertisements>
</s:Body>
</s:Envelope>
Update 2 (got it working!)
Finally, after about 9 hours we've got somewhere. I'll leave this here in for anybody else who has the utter misfortune of working with Lumesse (or another similar Java web service).
The main issue with the XML we are sending above is the Timestamp node under the Security node. The extraneous Nonce node it'll handle, presumably because it's not the first node under the Security node? Who knows (this is actually my first experience with SOAP / WCF in any form haha!).
So, the Timestamp node needs to go. If you need to use a standard binding like basicHttpBinding
or wsHttpBinding, you'll need to create a custom binding. Here's an example that mimics basicHttpBinding, apparently, taken from http://www.mikeobrien.net/blog/removing-wss-timestamp-from-wcf/
Example config:
<customBinding>
<binding name="MyBinding">
<security authenticationMode="UserNameOverTransport" includeTimestamp="false" />
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport maxReceivedMessageSize="26214400" />
</binding>
</customBinding>
Then just call the service as would, passing in the credentials (you can probably stores these in the web.config alongside the above, but I'm currently damned if I know how).
using (LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient client = new LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient())
{
client.ClientCredentials.UserName.UserName = "xxxx";
client.ClientCredentials.UserName.Password = "xxxx";
foreach (var ad in response.advertisementResult.advertisements)
{
#ad.jobTitle <br />
}
}
Thanks again.
I too am working with the same api and had the same issue. If you scroll down to the bottom of this article: http://www.hanselman.com/blog/BreakingAllTheRulesWithWCF.aspx Scott Hanselman removes the time-stamp through code rather than through the config.
I've got this WSDL(the service is not mine): http://soaptest.webapi-beta.gratka.pl/dom.html?wsdl
When I use WCF generated proxy the method tag in request soap message gets additional namespace definition like:
<q1:zaloguj xmlns:q1="http://soaptest.webapi-beta.gratka.pl/dom.html">
When I use PHP or proxy generated by wsdl.exe this doesn't happen.
I would like to ask, why does WCF do so, and is there possibility to change this behaviour (without using hand-made message modification in BeforeSendRequest)
Below I paste messages generated by PHP and WCF:
PHP one:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://soaptest.webapi-beta.gratka.pl/dom.html" 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:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:zaloguj>
<login xsi:type="xsd:string">login</login>
<haslo xsi:type="xsd:string">password</haslo>
<klucz_webapi xsi:type="xsd:string">key</klucz_webapi>
<id_kategoria xsi:type="xsd:int">382a</id_kategoria>
<wersja_webapi xsi:type="xsd:int">2</wersja_webapi>
</ns1:zaloguj>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
WCF one:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<q1:zaloguj xmlns:q1="http://soaptest.webapi-beta.gratka.pl/dom.html">
<login xsi:type="xsd:string">login</login>
<haslo xsi:type="xsd:string">password</haslo>
<klucz_webapi xsi:type="xsd:string">key</klucz_webapi>
<id_kategoria xsi:type="xsd:int">382</id_kategoria>
<wersja_webapi xsi:type="xsd:int">2</wersja_webapi>
</q1:zaloguj>
</s:Body>
</s:Envelope>
Are you facing any issues? As far as xml is concerned, both are equivalent. PHP code is declaring the namespace (xmlns:ns1="http://soaptest.webapi-beta.gratka.pl/dom.html") at the root element while WCF is declaring at the point where it is needed - I would believe that this is what WSDL's implementation would be - does not seem to be anything wrong in it.
I've got this WSDL(the service is not mine): http://soaptest.webapi-beta.gratka.pl/dom.html?wsdl
When I get response from method "pobierz_kategorie" I get exception like that
CommunicationException
Error in deserializing body of reply message for operation 'pobierz_kategorie'.
{"There is an error in XML document (2, 501)."} {"The specified type was not recognized: name='kategoria_drzewo', namespace='http://soap.webapi-beta.gratka.pl/dom.html', at ."}
I would like to ask how to modify WSDL or WCF configuration or generated proxy to make it working. When I use PHP to call that method it works so I guess that must be some XMLSerializer problem.
This the response message I get:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://soap.webapi-beta.gratka.pl/dom.html" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:pobierz_kategorieResponse>
<drzewo_kategorii SOAP-ENC:arrayType="ns1:kategoria_drzewo[48]" xsi:type="ns1:drzewo_kategorii">
<item xsi:type="ns1:kategoria_drzewo">
<id xsi:type="xsd:int">382</id>
<nazwa xsi:type="xsd:string">Dom</nazwa>
<dane xsi:type="xsd:string">0</dane>
<id_rodzic xsi:type="xsd:int">1</id_rodzic>
<poziom xsi:type="xsd:int">0</poziom>
<id_prasa xsi:type="xsd:int">2</id_prasa>
</item>
<item xsi:type="ns1:kategoria_drzewo">
<id xsi:type="xsd:int">8251</id>
<nazwa xsi:type="xsd:string">Pokoje</nazwa>
<dane xsi:type="xsd:string"/>
<id_rodzic xsi:type="xsd:int">382</id_rodzic>
<poziom xsi:type="xsd:int">1</poziom>
<id_prasa xsi:type="xsd:int">0</id_prasa>
</item>
</drzewo_kategorii>
</ns1:pobierz_kategorieResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
That is the request:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q1="http://soaptest.webapi-beta.gratka.pl/dom.html">
<s:Body s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<q1:pobierz_kategorie>
<sesja xsi:type="xsd:string">ad9e730460e334b916c95533c8da320003ac7e14</sesja>
<id_kategoria xsi:type="xsd:int">382</id_kategoria>
</q1:pobierz_kategorie>
</s:Body>
</s:Envelope>
I've retrieved that WSDL and the response message name does not match what you have above.
Where you have pobierz_kategorieResponse in the wsdl, it is pobierz_kategorie_wyjscie in the WSDL definition.
Message...
<message name="pobierz_kategorie_wyjscie">
<part name="drzewo_kategorii" type="tns:drzewo_kategorii"/>
</message>
Operation...
<operation name="pobierz_kategorie">
<input message="tns:pobierz_kategorie_wejscie"/>
<output message="tns:pobierz_kategorie_wyjscie"/>
</operation>
Maybe you need to regenerate your service reference or point it at a different endpoint.
EDIT
You may have to customize your reference.cs file (your generated WCF client code) to handle the soap array. I haven't done this myself but there is an SO article about it:-
Why won't .NET deserialize my primitive array from a web service?
I'm not 100% sure if this is the same issue as what you are having. It looks like you need to tell WCF explicitly - for this element, this is how you de-serialize the array. Sorry I can't be of more help.
EDIT #2
There is a mismatch between the wsdl published to you 'soaptest' and the actual implementation. Note the namespaces. Or, you've generated your code of a test wsdl endpoint and pointed it at a prod endpoint.See the namespaces below.Your request's namespaces looked like this:-
http://soaptest.webapi-beta.gratka.pl/dom.html
But the response's namespaces looked like this:-
http://soap.webapi-beta.gratka.pl/dom.html
That ain't gonna work!
Try generating your WCF client off
http://soap.webapi-beta.gratka.pl/dom.html?wsdl
instead of off
http://soaptest.webapi-beta.gratka.pl/dom.html?wsdl
QUICK SUMMARY:
Taleo has a single operation "Dispatcher WSDL" that is supposed to return a URL for use with its WebAPI WSDL ... the purpose is to allow the URL to be for all intents and purposes soft coded.
For operation getURL,
WcfTestClient.exe XML view shows that in the SOAP envelope a URL has been returned.
However, WcfTestClient.exe formatted view shows
Name: (return) Value: (null) Type: NullObject
My c# vs2010 .NET 4 code; taleoURL is always null
DispatcherAPIClient dispatcherClient = new DispatcherAPIClient("rpcrouter");
string taleoOrgCode = "TALEOSK";
string taleoURL = dispatcherClient.getURL(taleoOrgCode);
if (taleoURL == null) Console.WriteLine("null returned for " + taleoOrgCode);
This null is strange because here is the XML response from WcfTestClient.exe:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" />
<SOAP-ENV:Body>
<ns1:getURLResponse xmlns:ns1="urn:TBEDispatcherAPI" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:string">https://tbe.taleo.net/NA12/ats/services/rpcrouter</return>
</ns1:getURLResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
https://tbe.taleo.net/NA12/ats/services/rpcrouter is the value
of the <return> element.
To be able to use the above link, obviously I need taleoURL to be not null.
Pages 154-155 of the Taleo API guide say that
getURL's defined as string getURL(string orgCode)
http://tbe.taleo.net/products/TBE_API_Guide.pdf from
http://www.taleo.com/solutions/taleo-business-edition-web-integration-api
also
http://tbe.taleo.net/wsdl/DispatcherAPI.wsdl
http://tbe.taleo.net/wsdl/WebAPI.wsdl
Here's the Request/Response part of DispatcherAPI.wsdl:
<wsdl:message name="getURLRequest">
<wsdl:part name="orgCode" type="xsd:string" />
</wsdl:message>
<wsdl:message name="getURLResponse">
<wsdl:part name="getURLReturn" type="xsd:string" />
</wsdl:message>
My best guess is that I'm missing something.
This is a case where my own ignorance is not bliss.
MSDN and Google have not helped.
Thank you ... Gerry
I just started working with the Taleo WebAPI and I'm having the same issue.
Update: I changed the reference from a VS2010 C# Service Reference to an "old school" Web Reference and then it worked. I'm still working on a "real" solution, but that does work.