3
votes

I am working with an API that has two different URLs for specific types of functions. The first, is a transactional API that supports either JSON or SOAP requests. I have called all of the functions I need within this API using exclusively JSON calls and everything appears to be working perfectly fine.

The second is a reporting API used to locate and/or download reports. This API works exclusively with SOAP. I have not been able to get any function in this API working properly. I have attempted to contact the company's support group, but they do not have anybody who can assist me with API calls in ColdFusion. I have attempted two different ways to interface with this API and get access to the functions and have come up empty. Below are my examples and as much information as I can provide; our service provider's API and associated documentation are confidental, but I can answer some questions related to specific things that have to do with my code.

Way 1: Creating a webservice object.

The first way I tried to create this SOAP call was through a webservice object. Using the metadata exchange point URL, I passed it into the createObject function like this:

<cfset argStruct = structNew() />
<cfset argStruct['username'] = 'myusername' />
<cfset argStruct['password'] = 'mypassword' />
<cfset testSvc = createObject('webservice','https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/mex',argStruct) />

When I run this code, I get the following error message:

Cannot generate stub objects for web service invocation. Name: https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/mex. WSDL: https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/mex. javax.wsdl.WSDLException: WSDLException (at /wsdl:definitions/wsdl:import): faultCode=OTHER_ERROR: Unable to resolve imported document at 'https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/mex?wsdl=wsdl1', relative to 'brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/': java.io.IOException: Server returned HTTP response code: 401 for URL: https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/mex?wsdl=wsdl1

Since 401 errors are typically bad authorizations, I double-checked the address by calling the URL directly in the browser, where I was prompted with a UN/PW. I entered in my values, and was allowed to access the URL, recieving this XML in return:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="ReportingAPI" targetNamespace="https://https://brandnameapi.serviceprovider.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:tns="https://https://brandnameapi.serviceprovider.com" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:i0="https://https://brandnameapi.serviceprovider.com/ReportingAPI/soapBinding" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<wsdl:import namespace="https://https://brandnameapi.serviceprovider.com/ReportingAPI/soapBinding" location="https://https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/mex?wsdl=wsdl1"/>
<wsdl:types/>
<wsdl:service name="ReportingAPI">
    <wsdl:port name="BasicHttpBinding_IReportingAPI" binding="i0:BasicHttpBinding_IReportingAPI">
        <soap:address location="https://https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/soap"/>
    </wsdl:port>
</wsdl:service>

This is about as far as I've gotten. When I call the URL with my browser and pass the authentication information, I'm allowed to access the XML. When I try to do so with ColdFusion, I get 401 errors.

Way 2: cfhttp request calls

When I switched to using cfhttp, I seemed to get a little further. When I use this:

<cfhttp url="https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/mex"
username="myusername" password="mypassword" method="get" result="httpResponse" 
timeout="300">
</cfhttp>

httpResponse return appropriate page information, and httpResponse.filecontent returns the same XML I recieved when I called it directly in my browser.

Going one step further, I took the SOAP URL and attempted to call a function in the API that returns a list of available report files. I used the same known-working process I used for all of my JSON calls in the transaction API:

<cfhttp url="https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/soap/queryAvailableReportFiles"
username="myusername" password="mypassword" method="post" 
result="httpResponse" timeout="300">
<cfhttpparam type="formfield" name="typeOfReport" value="DailyCSVFile" />
</cfhttp>

When I run this code, I get a status code of 415 and an error of, 'Cannot process the message because the content type 'application/x-www-form-urlencoded' was not the expected type 'multipart/related; type="application/xop+xml"'

When I add this line between my cfhttps:

<cfhttpparam type="header" name="Content-Type" value='multipart/related; type="application/xop+xml"' />

I get the same error as directly above, but the status code changes to 400. I have not included everything I've tried to do, only where I'm at right now. I will answer as many questions as I can and will reperform steps as directed to find a solution to this problem.

Update: As requested, I have changed the cfhttp call to attempt to pass XML instead of a form field. My XML code is ripped directly from the API documentation that has an example of a raw data for a request from the API for a different function:

<cfsavecontent variable="soapBody">
<cfoutput>
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
        <s:Body>
            <queryAvailableReportFiles
xmlns="https://brandnameapi.sandbox.serviceprovider.com/contract">
                <fileName>DailyCSVFile</fileName>
            </queryAvailableReportFiles>
        </s:Body>
    </s:Envelope>
</cfoutput>
</cfsavecontent>

<cfhttp url="https://prismproapi.sandbox.koretelematics.com/4/ReportingAPI.svc/soap/queryAvailableReportFiles" username="vfapi" password="bPzqQyK3" method="post" result="httpResponse" timeout="300">
<cfhttpparam type="xml" value="#trim(soapBody)#" />
</cfhttp>

To be fair, I have no idea if I'm doing that right. The error message that returns from it is, "The message with To https://prismproapi.sandbox.koretelematics.com/4/ReportingAPI.svc/soap/queryAvailableReportFiles cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree." I also get a 500 error.

2
Have you tried using SoapUI to the web service? Whenever I have issues with SOAP I always test with SoapUI. If you can get it to work with that then it may point to what is needed for ColdFusion. Also, I always end up using cfhttp to make my web service calls. I believe it gives you more control over the configuration aspects needed for most web services. Is the reporting web service expecting XML content?Miguel-F
What happens if you append ?wsdl to the url in your createobject()?Dan Bracuk
@Miguel-F, I haven't used SoapUI in any capacity. I've never heard of it before. The documentation doesn't state what type of content is being expected so I have to assume that the content type is what is specified as what's being expected.K_Cruz
@DanBracuk - adding ?wsdl to the end of the URL generates the same error message.K_Cruz
SoapUI is a great tool for working with SOAP services. Did you see the other part of my comment - is the service expecting XML content? Your last request appears to be close but failed with a 400 like it was expecting something. I typically have to build my xml SOAP request and then send it with the cfhttp call. But you will need to know what the service is wanting in order to do that.Miguel-F

2 Answers

3
votes

Here is an example of how I typically compose and execute my SOAP requests. Note that you will need to modify the SOAP body to fit your API's needs. Hopefully this will help lead you in the right direction.

By the way, Ben Nadel has an excellent right up on Making SOAP Web Service Requests With ColdFusion And CFHTTP

Here is my sample code:

<!--- Compose SOAP message to send to Web Service --->
<cfsavecontent variable="soapRequest">
    <?xml version="1.0" encoding="UTF-8" ?> 
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:example="http://www.domain.com/soap/example/">
        <soapenv:Header/>
        <soapenv:Body>
          <example:ReportAPI>
             <typeOfReport>DailyCSVFile</typeOfReport>
          </example:ReportAPI>
        </soapenv:Body>
    </soapenv:Envelope>
</cfsavecontent>

<!--- Send SOAP request to the Web Service --->
<cfhttp url="https://brandnameapi.sandbox.serviceprovider.com/vernum/ReportingAPI.svc/soap/queryAvailableReportFiles" username="myusername" password="mypassword" method="post" result="httpResponse" timeout="300">
    <cfhttpparam type="header" name="content-type" value="text/xml" />
    <cfhttpparam type="header" name="content-length" value="#Len(Trim(soapRequest))#" />
    <cfhttpparam type="header" name="charset" value="utf-8" />
    <cfhttpparam type="xml" name="message" value="#Trim(soapRequest)#" />
</cfhttp> 
0
votes

There was a problem with their API. No code change would have addressed this issue.