404 Not Found error in SOAP web service request via CFHTTP - coldfusion

I am having a lot of trouble communicating with a web service for our membership database. I get a valid result from my SOAP envelope when using SoapUI. But when I try to send the same envelope using CFHTTP and CF9 (I know, I know), I get a "404 Not Found" error no matter what I try to do. Accessing the URL in a browser gives me an "access denied" error.
Here is what I have in cfsavecontent:
<cfsavecontent variable="soapBody">
<cfoutput>
<?xml version=“1.0” encoding=“utf-8”?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://membersuite.com/schemas" xmlns:con="http://membersuite.com/contracts">
<soapenv:Header>
<sch:ConciergeRequestHeader>
<!--Optional:-->
<sch:BrowserId></sch:BrowserId>
<!--Optional:-->
<sch:SessionId></sch:SessionId>
<sch:AccessKeyId>MyAccessKey</sch:AccessKeyId>
<!--Optional:-->
<sch:AssociationId>MyAssociationID</sch:AssociationId>
<sch:Signature>MySignature</sch:Signature>
</sch:ConciergeRequestHeader>
</soapenv:Header>
<soapenv:Body>
<con:LoginToPortal>
<!--Optional:-->
<con:portalUserName>username</con:portalUserName>
<!--Optional:-->
<con:portalPassword>password</con:portalPassword>
</con:LoginToPortal>
</soapenv:Body>
</soapenv:Envelope>
</cfoutput>
</cfsavecontent>
And here is my cfhttp:
<cfhttp url="https://soap.membersuite.com/mex" method="post" useragent="#CGI.http_user_agent#">
<cfhttpparam type="header" name="charset" value="utf-8">
<cfhttpparam type="header" name="mimetype" value="application/xml" />
<cfhttpparam type="header" name="content-type" value="text/xml">
<cfhttpparam type="header" name="SOAPAction" value="http://membersuite.com/contracts/IConciergeAPIService/LoginToPortal" />
<cfhttpparam type="header" name="accept-encoding" value="no-compression" />
<cfhttpparam type="header" name="content-length" value="#len(trim(soapBody))#">
<cfhttpparam type="xml" name="soapenv" value="#trim(soapBody)#" />
</cfhttp>
Dumping cfhttp gives me this error:
It seems obvious to me that the remote server is telling me it cannot find what I am asking it for. But it is the right endpoint, and I can see it in the WSDL:
<wsdl:operation name="LoginToPortal">
<wsdl:input wsaw:Action="http://membersuite.com/contracts/IConciergeAPIService/LoginToPortal" message="tns:IConciergeAPIService_LoginToPortal_InputMessage"/>
<wsdl:output wsaw:Action="http://membersuite.com/contracts/IConciergeAPIService/LoginToPortalResponse" message="tns:IConciergeAPIService_LoginToPortal_OutputMessage"/>
</wsdl:operation>
I have also added the SSL cert from the web service page to my CF keystone; no change in error message. I am at my wit's end and would appreciate any suggestions! Again, I am getting expected results back via SoapUI. Thank you all very much!

Going strictly off the SoapUI request, it seems to work if the target URL is changed to https://soap.membersuite.com/. Using bogus credentials, the response changes to "Invalid Access Key ID specified" instead of 404.
<cfhttp url="https://soap.membersuite.com/" method="post" useragent="#CGI.http_user_agent#">
<cfhttpparam type="header" name="Content-Type" value="text/xml; charset=utf-8" />
<cfhttpparam type="header" name="SOAPAction" value="http://membersuite.com/contracts/IConciergeAPIService/LoginToPortal" />
<cfhttpparam type="header" name="Accept-Encoding" value="no-compression" />
<cfhttpparam type="header" name="Content-Length" value="#len(trim(soapBody))#">
<cfhttpparam type="body" value="#trim(soapBody)#" />
</cfhttp>

Related

cfhttp returning a connection failure for a network url that a browser can reach via both http and https

I am using cfhttp to try to pull some json from one of our organizations APIs. I have the code working when trying to pull example json from one of our own servers, but when I point it to the API itself I receive a connection failure. This is the code:
<cfhttp method="get" url="http://[url]/api/menu">
<cfhttpparam type="URL" name="id" value="[guid]">
</cfhttp>
<cfset FileWrite("#application.tempDirPath#/temp.json", CFHTTP.FileContent)>
I have replaced the real values with [url] and [guid].
I have tried accessing this url in a browser at https://[url]/api/menu?id=[guid] and it works perfectly fine, indicating that my machine has no problem reaching that url. The url is a server in our network that I can access as long as I'm on the company VPN.
Things that I have tried:
The live code should point to https. I've tried both http and https.
I've tried hitting the home page at that same url and it also results in a connection failure, but works in a browser.
I've tried this same code with http://www.google.com, and that downloads the Google html without issue.
I've tried restarting ColdFusion and my machine.
I've tried disabling compression via headers:
<cfhttp method="get" url="http://[url]/api/menu">
<cfhttpparam type="header" name="Accept-Encoding" value="deflate;q=0" />
<cfhttpparam type="header" name="TE" value="deflate;q=0" />
<cfhttpparam type="URL" name="id" value="[guid]">
</cfhttp>
I do not have access to the target server. I can ask those who do have access for help, but their conclusion at this point is "this is a ColdFusion problem.", so I'm not sure how much help I can get. This makes it impossible for me to check for Firewall rules or user-agent restrictions; suggestions on ways to test for those things locally would be welcome.
Edit
Per comments on this question I determined that http is just redirecting to https at that address. So I followed the instructions provided at ColdFusion https connection failure to import the certificate. After doing so I followed the instructions to verify that the cert was imported successfully. I then restarted CF Server.
After that I ran my code again and dumped the cfhttp struct as a whole into the temp file. This was the result:
<?xml version="1.0" encoding="UTF-8"?>
<STRUCT ID="1">
<ENTRY NAME="Errordetail" TYPE="STRING">I/O Exception: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target</ENTRY>
<ENTRY NAME="Mimetype" TYPE="STRING">Unable to determine MIME type of file.</ENTRY>
<ENTRY NAME="Statuscode" TYPE="STRING">Connection Failure. Status code unavailable.</ENTRY>
<ENTRY NAME="Filecontent" TYPE="STRING">Connection Failure</ENTRY>
<ENTRY NAME="Responseheader" TYPE="STRUCT">
<STRUCT ID="2" />
</ENTRY>
<ENTRY NAME="Text" TYPE="BOOLEAN">true</ENTRY>
<ENTRY NAME="Charset" TYPE="STRING" />
<ENTRY NAME="Header" TYPE="STRING" />
</STRUCT>

Passing a parameter in CFHTTP URL string

I am using cfhttp to post a series of user supplied parameters to an API. I am passing all of the parameters with <cfhttparam> except one: {myID}. Since the {myID} value will also be supplied by the user, I am wondering how can I make sure it gets passed inside the URL string?
<cfhttp url="http://abc.xyz.com/webservice/{myID}/add/multiple"
method="POST" result="returnStruct" >
<cfhttpparam name="Accept" type="header" value="application/json" />
<cfhttpparam type="header" name="datetime" value="#datetime#" />
<cfhttpparam type="header" name="authorization" value="#authorization#" />
<cfhttpparam type="formfield" name="myAPI" value="1" />
<cfhttpparam type="formfield" name="param1" value="1000" />
<cfhttpparam type="formfield" name="param2" value="myname#email.com" />
<cfhttpparam type="formfield" name="param3" value="2" />
<cfhttpparam type="formfield" name="param4" value="Tester" />
</cfhttp>
The client aplication should have the user's data from a previous request, if you pass it like
http://abc.xyz.com/webservice/user_id/add/multiple the server assumes that user_id is the parameter you call {myID}
Good luck

Calling a SOAP Web Service Function

I am trying to use a web service to save data. The web service URL is something like:
http://example.com/webservice1/order.asmx
When I want to save an order, I will send a SOAP Envelope and have to call the function saveorder. How can I specify in ColdFusion that I want to call this function?
You'll have to use cfhttp and build up your SOAP Envelope and pass it through with your cfhttp request.
This is something I wrote some time ago:
<cfscript>
savecontent variable="local.sSoap" {
WriteOutput("
<?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>
#arguments.sData#
</soap:Body>
</soap:Envelope>
");
}
</cfscript>
<cfhttp url="https://apiconnector.com/API.asmx" method="post" result="httpResponse">
<cfhttpparam type="header" name="SOAPAction" value="http://apiconnector.com/#arguments.sMethod#"/>
<cfhttpparam type="header" name="accept-encoding" value="no-compression"/>
<cfhttpparam type="xml" value="#trim( local.sSOAP )#"/>
</cfhttp>
You'll obviously have to make changes to suit your own needs, but the gist is there.

Consume SOAP web service having complex types

I am new to ColdFusion and need to write code to consume a SOAP based web service.
Any links/pointers/examples for consuming SOAP based web service which has complex types will help.
When I am writing the code to consume the below web service in ColdFusion, how should I handle operation name, input msg, and complex types? Just need some pointers to get started.
XSD is something like :
<!-- S Request -->
<xs:complexType name="SRequestHeader">
+ <xs:sequence>
+ <xs:element name="sID" minOccurs="1" maxOccurs="1"> </xs:element>
+ <xs:element name="orderNumber" minOccurs="1" maxOccurs="1"> </xs:element>
+ <xs:element name="dateCreated" minOccurs="1" maxOccurs="1"> </xs:element>
</xs:complexType>
- <xs:complexType name="SOrderLine">
- <xs:sequence>
- <xs:element name="lineNumber" minOccurs="1" maxOccurs="1"> </xs:element>
- <xs:element name="recordType" minOccurs="1" maxOccurs="1"> </xs:element>
- <xs:element name="dueDate" minOccurs="1" type="xs:dateTime" />
</xs:complexType>
......
WSDL has :
<WL:portType name="SPortType">
- <WL:operation name="newOrder">
<WL:input message="WL:newOrderRequest" name="newOrderRequest" />
<WL:output> message="W:newOrderResponse" name="newOrderResponse" />
<WL:fault> message="WL:WSException" name="WSException" />
</WL:operation>
I am using something like:
<soapenv:Body>
<newOrder>
<soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<sor:newOrderRequest>
<sor:SOrderRequest>
<sor:sID>S123</sor:sID> ....
And finally...
<cfhttp url="http://xxxx:123/YYY/SService" method="post" timeout="118"
throwonerror="yes">
<cfhttpparam type="header" name="content-type" value="text/xml">
<cfhttpparam type="header" name="SOAPAction" value="">
<cfhttpparam type="header" name="content-length" value="#len(soap)#">
<cfhttpparam type="header" name="charset" value="utf-8">
<cfhttpparam type="xml" name="message" value="#trim(soap)#">
</cfhttp>
Getting 500 Internal Server Error on this line :
<cfhttpparam type="xml" name="message" value="#trim(soap)#">
You have not shared complete code so some assumptions must be made.
Ben Nadel has a wonderful write up on this topic. You should definitely read this first: Making SOAP Web Service Requests With ColdFusion And CFHTTP
Whenever I interact with SOAP services I usually end up using something similar to the following. It is very similar to the code fragment that you shared but you did not show (among other things) the content being wrapped in <cfsavecontent> tags to store the XML in the soap variable before making the <cfhttp> request. This could be your problem? The following is just an example to get you going.
<cfsavecontent variable="soap">
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<ns1:newOrder xmlns:ns1="urn:TripFlow" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<sID>001</sID>
<orderNumber>12345</orderNumber>
<dateCreated>07/31/2013</dateCreated>
</ns1:newOrder>
</soapenv:Body>
</soapenv:Envelope>
</cfsavecontent>
<!--- Invoke web service to send message--->
<cfhttp url="http://xxxx:123/YYY/SService" method="post" timeout="10">
<cfhttpparam type="header" name="content-type" value="text/xml" />
<cfhttpparam type="header" name="SOAPAction" value="""SService-method-name-here""" />
<!---<cfhttpparam type="header" name="accept-encoding" value="no-compression" /> sometimes this is needed --->
<cfhttpparam type="header" name="content-length" value="#len(soap)#" />
<cfhttpparam type="header" name="charset" value="utf-8" />
<cfhttpparam type="xml" name="message" value="#trim(soap)#" />
</cfhttp>
Another invaluable tool when working with web services is soapUI. That should definitely be a part of your toolkit as well. You can build your request using soapUI and check the responses. Once you have it working with soapUI you can copy your request into your ColdFusion code.
Thanks #Miguel-F .Finally used SOAPUI and understood what was going wrong.
Had set parameter throwonerror="Yes" .Because of this ParseException was going to error block rather than getting captured in code.
On setting throwonerror="No", the code finally started working and reading the response tags.

ColdFusion soap client

The following is a sample SOAP 1.2 request and response. The placeholders shown need to be replaced with actual values.
POST /CommunicationOfficeService1_0/CompanyAccountXmlService.asmx HTTP/1.1
Host: webservices.nbs.rs
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Header>
<AuthenticationHeader xmlns="http://communicationoffice.nbs.rs">
<UserName>string</UserName>
<Password>string</Password>
<LicenceID>guid</LicenceID>
</AuthenticationHeader>
</soap12:Header>
<soap12:Body>
<GetCompanyAccountByNationalIdentificationNumber xmlns="http://communicationoffice.nbs.rs">
<nationalIdentificationNumber>long</nationalIdentificationNumber>
</GetCompanyAccountByNationalIdentificationNumber>
</soap12:Body>
</soap12:Envelope>
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetCompanyAccountByNationalIdentificationNumberResponse xmlns="http://communicationoffice.nbs.rs">
<GetCompanyAccountByNationalIdentificationNumberResult>string</GetCompanyAccountByNationalIdentificationNumberResult>
</GetCompanyAccountByNationalIdentificationNumberResponse>
</soap12:Body>
</soap12:Envelope>
I have generated the ColdFusion code that looks like this
<cfsavecontent variable="soapBody">
<cfoutput>
POST /CommunicationOfficeService1_0/CompanyAccountXmlService.asmx HTTP/1.1
Host: webservices.nbs.rs
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Header>
<AuthenticationHeader xmlns="http://communicationoffice.nbs.rs">
<UserName>my_username</UserName>
<Password>my_password</Password>
<LicenceID>My_licence_id</LicenceID>
</AuthenticationHeader>
</soap12:Header>
<soap12:Body>
<GetCompanyAccountByNationalIdentificationNumber xmlns="http://communicationoffice.nbs.rs">
<nationalIdentificationNumber>20774550</nationalIdentificationNumber>
</GetCompanyAccountByNationalIdentificationNumber>
</soap12:Body>
</soap12:Envelope>
</cfoutput>
</cfsavecontent>
<!---
Now that we have our SOAP body defined, we need to post it as
a SOAP request to the Campaign Monitor website. Notice that
when I POST the SOAP request, I am NOT required to append the
"WSDL" flag to the target URL (this is only required when you
actually want to get the web service definition).
--->
<cfhttp
url="https://webservices.nbs.rs/CommunicationOfficeService1_0/CompanyAccountXmlService.asmx"
method="post"
result="httpResponse">
<!---
Most SOAP action require some sort of SOAP Action header
to be used.
--->
<cfhttpparam
type="header"
name="SOAPAction"
value="http://communicationoffice.nbs.rs/GetCompanyAccountByNationalIdentificationNumber"
/>
<!---
I typically use this header because CHTTP cannot handle
GZIP encoding. This "no-compression" directive tells the
server not to pass back GZIPed content.
--->
<cfhttpparam
type="header"
name="accept-encoding"
value="no-compression"
/>
<!---
When posting the SOAP body, I use the CFHTTPParam type of
XML. This does two things: it posts the XML as a the BODY
and sets the mime-type to be XML.
NOTE: Be sure to Trim() your XML since XML data cannot be
parsed with leading whitespace.
--->
<cfhttpparam
type="xml"
value="#trim( soapBody )#"
/>
</cfhttp>
<!---
When the HTTP response comes back, our SOAP response will be
in the FileContent atribute. SOAP always returns valid XML,
even if there was an error (assuming the error was NOT in the
communication, but rather in the data).
--->
<cfif find( "200", httpResponse.statusCode )>
<!--- Parse the XML SOAP response. --->
<cfset soapResponse = xmlParse( httpResponse.fileContent ) />
<!---
Query for the response nodes using XPath. Because the
SOAP XML document has name spaces, querying the document
becomes a little funky. Rather than accessing the node
name directly, we have to use its local-name().
--->
<cfset responseNodes = xmlSearch(
soapResponse,
"//*[ local-name() = 'Subscriber.AddAndResubscribeResult' ]"
) />
<!---
Once we have the response node, we can use our typical
ColdFusion struct-style XML node access.
--->
<cfoutput>
Code: #responseNodes[ 1 ].Code.xmlText#
<br />
Success: #responseNodes[ 1 ].Message.xmlText#
</cfoutput>
</cfif>
I have none results . The web service uses this is the web site
This is the wsdl file
Serbian National Bank Web Service
How to make this web service run
I've modified a function I've used in the past to pass data form ColdFusion to Dynamics GP via soap.
<cffunction name="callWebService" access="public" output="false">
<cfargument name="soap" type="xml" required="true">
<cfhttp url="https://webservices.nbs.rs/CommunicationOfficeService1_0/CompanyAccountXmlService.asmx" method="post">
<cfhttpparam type="header" name="content-type" value="application/soap+xml">
<cfhttpparam type="header" name="SOAPAction" value="http://communicationoffice.nbs.rs/GetCompanyAccountByNationalIdentificationNumber">
<cfhttpparam type="header" name="accept-encoding" value="no-compression">
<cfhttpparam type="header" name="content-length" value="#len(Arguments.soap)#">
<cfhttpparam type="header" name="charset" value="utf-8">
<cfhttpparam type="xml" name="message" value="#trim(Arguments.soap)#">
</cfhttp>
<cfreturn Trim(cfhttp.FileContent)>
</cffunction>
I believe I've updated the <cfhttpparam />s necessary for the web service you've linked. I would recommend using a software called SOAP UI to test your connection to the service and SOAP XML body outside of ColdFusion. In my above function, the argument soap would be your saved content "soapBody".
One thing needs to be fixed in your soapBody. The text prior to the start of XML is information regarding data that needs to be in the HTTP header, and I've only seen these values imputed as <cfhttpparam />.
The reason I think you may not be getting the desired 200 response with your above code is that the XML body is incorrectly formatted with the WSDL HTTP header example text. I also did not see the length of the body being declared as well.
While trying to set up your connection try just dumping httpResponse out until you start seeing valid soap responses from the server.