18 Replies Latest reply on Oct 25, 2010 5:31 PM by TheRealAgentK

    Webservice Driving me crazy!

    kenji776 Level 1

      Hey all,

      Trying to write a quick callout to a web service from a provider called echosign. Getting the classic "webservice operations with these parameters cannot be found"

       

      This is the URL for the webservice

      http://www.echosign.com/services/EchoSignDocumentService9?wsdl

       

      This is the documentation on the method I am trying to invoke

      https://secure.echosign.com/static/apiv9/apiMethods9.jsp#sendDocument

       

      Here you can see exactly what I am getting

      http://portal.fpitesters.com/echosign.cfm?cmd=sendDocument

       

      This is my code thus far

      <!--- User specific variables, change in your isntance ---->
      <cfset apiKey = "XA7R8YXV4N4K79">
      <cfset strFilePath = "C:\website\portal\test.pdf" />
      <cfset agreementName = "Test File">
      <cfset fileName = "Test File">

      <cfsavecontent variable="message">
      Put any message you like here. Probably some HTML content
      </cfsavecontent>

      <!--- Url for the web service, shouldn't need to touch this --->
      <cfset wsdl ="http://www.echosign.com/services/EchoSignDocumentService9?wsdl">

      <!---- Remove me later, just a placeholder for now ---->
      <cfset userLogin.UserEmail = "sometest@nowhere.com">


      <!--- Don't touch below here, unless you really want to. Then I guess have at, but ya know you might break it or something ---->
      <cfset arguments = structnew()>
      <cfset arguments.apiKey = apikey>

      <!--- first lets run a test to make sure we can actually communicate --->
      <cftry>
           Result of test API call:
           <cfinvoke webservice="#wsdl#" method="testPing" attributecollection="#arguments#" returnvariable="myTestResult"/>
                <cfoutput><strong>#myTestResult.getMessage()#</strong></cfoutput>
           <cfcatch type="any">
                <strong>Calling webservice failed.</strong><br>
                <cfdump var="#cfcatch#">
           </cfcatch>
      </cftry>

      <br><br>

      <!--- K lets try and actually send a document now ---->

      <!--- Make sure the file exists. otherwise abort --->
      <cfif fileexists(strFilePath)>
           <cffile action="readbinary" file="#strFilePath#" variable="objBinaryData" />
           <cfelse>
                File could not be found. Please check file path.
           <cfabort>
      </cfif>

      <!--- create the documentCreationInfo key that holds all the needed data for the webservice call ---->
      <cfset arguments.DocumentCreationInfo = structnew()>

      <!--- They seem to want a string array for emails. I think that means an actual array, so create an array and jam the
            email from above in the first element ---->
      <cfset arguments.DocumentCreationInfo.tos = arraynew(1)>
      <cfset arguments.DocumentCreationInfo.tos[1] = userLogin.UserEmail>

      <!--- Just some more required keys --->
      <cfset arguments.DocumentCreationInfo.name = agreementName>
      <cfset arguments.DocumentCreationInfo.message = message>

      <!--- this is where it gets a little crazy. There is a key called fileInfos. That seems to be an array. Within each array
            element they seem to want another structure with 2 keys, the filename and the file. Now I'm not sure if they actually
             want an array of structures here or what. It would make sense, seeing as you are able to send more than one file at a time
             and if it wasn't broken into array elements you wouldn't be able to do that --->
      <cfset arguments.DocumentCreationInfo.fileInfos = arraynew(1)>
      <cfset arguments.DocumentCreationInfo.fileInfos[1] = structnew()>

      <cfset arguments.DocumentCreationInfo.fileInfos[1].fileName = fileName>

      <!---The file data needs to be base64 encoded, so do that --->
      <cfset arguments.DocumentCreationInfo.fileInfos[1].file = ToBase64(objBinaryData)>

      <!--- Some other needed params here --->
      <cfset arguments.DocumentCreationInfo.signatureType = "ESIGN">
      <cfset arguments.DocumentCreationInfo.signatureFlow = "SENDER_SIGNATURE_NOT_REQUIRED">

      <!--- for debugging so we can see what we are sending ---->
      <cfdump var="#arguments#">

      <!--- Try to call the web service with the provided arguments --->
      <cftry>
           <cfinvoke webservice="#wsdl#" method="#url.cmd#" attributecollection="#arguments#" returnvariable="myresult"/>

           <!--- if it works, dump out the return object, so we can figure out what they are giving us and extract what is needed ---->
           <cfdump var="#myresult#">
           
           <!--- Otherwise the thing bombed, so just print that out for debugging purposes --->
           <cfcatch type="any">
                Calling webservice failed.<br>
                <cfdump var="#cfcatch#">
           </cfcatch>
      </cftry>     

       

      Here is the provided PHP sample code that I am trying to imitate

       

      function cmd_send() {
          global $Url, $ApiKey, $S;

          list($filename, $recipient) = reset(func_get_args());

          $r = $S->sendDocument(array(
              'apiKey' => $ApiKey,
              'documentCreationInfo' => array(
                      'fileInfos' => array(
                          'FileInfo' => array(
                              'file'     => file_get_contents($filename),
                              'fileName' => $filename,
                          ),
                      ),
                      'message' => "This is neat.",
                      'name'    => "Test from SOAP-Lite: $filename",
                      'signatureFlow' => "SENDER_SIGNATURE_NOT_REQUIRED",
                      'signatureType' => "ESIGN",
                      'tos' => array( $recipient ),
                  ),
              )
          );

          print "Document key is: {$r->documentKeys->DocumentKey->documentKey}\n";
      }

       

       

      Thanks for any help. I imagine I'm just doing something dumb, but hell if I know what it is. Thanks!

        • 1. Re: Webservice Driving me crazy!
          Dan Bracuk Level 5

          Your willingness to write exceeds my willingness to read.  At what point does it stop working?

           

          Were you able to invoke TestPing?

           

          At this stage, forget all this cftry/cfcatch stuff.  Keep it so simple that even I can understand it.  In other words, what method were you trying to invoke, what were the required input parameters, what did you send it, and what were the results?

          • 2. Re: Webservice Driving me crazy!
            kenji776 Level 1

            Haha, sorry to be so long winded, figured better to much info than not enough.

            It breaks at the last web service call.

            The testPing is successful, and does return the expected results.

             

            This line does not run

            <cfinvoke webservice="#wsdl#" method="#url.cmd#" attributecollection="#arguments#" returnvariable="myresult"/>

             

            I imagine it has something to do with how I am structuring the variables I am passing in. I always screw it up, sometimes the webservices want arrays, sometimes they want structures, and I never can seem to tell. Of course since I get the variable typing wrong, it says there is no method with that name and parameters.

             

            The catch try stuff was just there to stop a site wide error handler from kicking in that I have on my test server. Thanks for looking.

            • 3. Re: Webservice Driving me crazy!
              Dan Bracuk Level 5

              What's an attributecollection?

              • 4. Re: Webservice Driving me crazy!
                kenji776 Level 1

                An attribute collection is just a bunch of data to be passed to the method. You can use it instead of using cfinvokearguments. Check it out.

                 

                http://livedocs.adobe.com/coldfusion/6/CFML_Reference/Tags-pt163.htm

                 

                Unless you are trying to force me to think about some dumb mistake I made, and I'm being too dense to catch your lesson

                • 5. Re: Webservice Driving me crazy!
                  existdissolve Level 2

                  Isn't attributecollection for specifying--via another structure--the attributes of the tag itself, not the arguments that it passes?  I think you're wanting argumentcollection

                  • 6. Re: Webservice Driving me crazy!
                    kenji776 Level 1

                    I'll have to test it out. If your right, I'm going to be very embarassed for being so dumb. Thanks for the suggestion none the less. I'll make the change and let you know how it goes.

                    • 7. Re: Webservice Driving me crazy!
                      BKBK Adobe Community Professional & MVP

                      attributecollection="#arguments#" ...

                      The suggestion about argumentsCollection notwithstanding, you are passing a struct to the service*. This assumes the web service knows what a Coldfusion struct is.

                       

                      "webservice operations with these parameters cannot be found"

                       

                      You get this message probably because the web service's method is unable to read a struct. In place of attributecollection, pass the individual parameters, for example:

                       

                      apiKey=1234 senderInfo="BKBK's info"

                       

                       

                       

                      * Passing the parameter attributeCollection to the web service is technically not incorrect. In fact, you may pass any user-defined parameter to a web servoce. However, the service must know what you are sending it.

                      • 8. Re: Webservice Driving me crazy!
                        kenji776 Level 1

                        Thanks a lot for the suggestions, I tried updating my web service invocation to this

                         

                             <cfinvoke webservice="http://www.echosign.com/services/EchoSignDocumentService9?wsdl" method="sendDocument" returnvariable="myresult">
                                  <cfinvokeargument name="apiKey" value="#apiKey#">
                                  <cfinvokeargument name="documentCreationInfo" value="#DocumentCreationInfo#">
                             </cfinvoke>     
                        

                         

                        alas, same results. Any other thoughts? I'll keep playing around with it, but I don't have any great ideas.

                         

                        If anyone wants to play around with it, here is the full new updated code.

                         

                        <!--- User specific variables, change in your isntance ---->
                        <cfset apiKey = "XA7R8YXV4N4K79">
                        <cfset strFilePath = "C:\website\portal\test.pdf" />
                        <cfset agreementName = "Test File">
                        <cfset fileName = "Test File">
                        
                        <cfsavecontent variable="message">
                        Put any message you like here. Probably some HTML content
                        </cfsavecontent>
                        
                        <!---- Remove me later, just a placeholder for now ---->
                        <cfset userLogin.UserEmail = "sometest@nowhere.com">
                        
                        <!--- Make sure the file exists. otherwise abort --->
                        <cfif fileexists(strFilePath)>
                             <cffile action="readbinary" file="#strFilePath#" variable="objBinaryData" />
                             <cfelse>
                                  File could not be found. Please check file path.
                             <cfabort>
                        </cfif>
                        
                        <!--- create the documentCreationInfo struct that holds all the document data for the webservice call ---->
                        <cfset DocumentCreationInfo = structnew()>
                        
                        <!--- They seem to want a string array for emails. I think that means an actual array, so create an array and jam the
                              email from above in the first element ---->
                        <cfset DocumentCreationInfo.tos = arraynew(1)> 
                        <cfset DocumentCreationInfo.tos[1] = userLogin.UserEmail>
                        
                        <!--- Just some more required keys --->
                        <cfset DocumentCreationInfo.name = agreementName>
                        <cfset DocumentCreationInfo.message = message>
                        
                        <!--- this is where it gets a little crazy. There is a key called fileInfos. That seems to be an array. Within each array
                              element they seem to want another structure with 2 keys, the filename and the file. Now I'm not sure if they actually
                               want an array of structures here or what. It would make sense, seeing as you are able to send more than one file at a time
                               and if it wasn't broken into array elements you wouldn't be able to do that --->
                        <cfset DocumentCreationInfo.fileInfos = arraynew(1)>
                        <cfset DocumentCreationInfo.fileInfos[1] = structnew()>
                        
                        <cfset DocumentCreationInfo.fileInfos[1].fileName = fileName>
                        
                        <!---The file data needs to be base64 encoded, so do that --->
                        <cfset DocumentCreationInfo.fileInfos[1].file = ToBase64(objBinaryData)>
                        
                        <!--- Some other needed params here --->
                        <cfset DocumentCreationInfo.signatureType = "ESIGN">
                        <cfset DocumentCreationInfo.signatureFlow = "SENDER_SIGNATURE_NOT_REQUIRED">
                        
                        <!--- Try to call the web service with the provided arguments --->
                        <cftry>
                             <cfinvoke webservice="http://www.echosign.com/services/EchoSignDocumentService9?wsdl" method="sendDocument" returnvariable="myresult">
                                  <cfinvokeargument name="apiKey" value="#apiKey#">
                                  <cfinvokeargument name="documentCreationInfo" value="#DocumentCreationInfo#">
                             </cfinvoke>     
                                  
                             <!--- if it works, dump out the return object, so we can figure out what they are giving us and extract what is needed ---->
                             <cfdump var="#myresult#">
                             
                             <!--- Otherwise the thing bombed, so just print that out for debugging purposes --->
                             <cfcatch type="any">
                                  Calling webservice failed.<br>
                                  <cfdump var="#cfcatch#">
                             </cfcatch>
                        </cftry>     
                        
                        
                        
                        • 9. Re: Webservice Driving me crazy!
                          kenji776 Level 1

                          Also, not sure if it would help anyone debug this, but it did come with sample code for C#, Ruby, PHP,and Java. So if you know any of those languages and want to see how they are doing it with those, I can post the relevent code. From my limited understanding of these other languages it looks like I am following what they are doing, but I am really not sure.

                          • 10. Re: Webservice Driving me crazy!
                            insuractive Level 3

                            Consuming web services that use complex data types in CF can be trying.  Sometimes it just seems easier to create the SOAP XML from scratch, send using CFHTTP and parse the XML response on the response.  Google should be able to turn up some good sample code for how to customize the SOAP XML based on the WSDL file you are working with.  Otherwise, make sure each element (and sub-element) of your request structure matches exactly to the definition in the WSDL file for the DocumentCreationInfo object (including optional, non-required elements as well).

                            • 11. Re: Webservice Driving me crazy!
                              kenji776 Level 1

                              Oh man, I'd really prefer to not have to write the SOAP for this. I've never done that before, and it seems pretty tedious/time consuming.I guess I don't know for sure though. I'll start looking that way, but it's aggrevating to know that I am probably very very close and to have to scrap this approach. Anyone else have any other ideas as for what is up with this time? Again, I do have working sample code from several other languges that could be used as a 'rosetta' stone of sorts, for anyone who gets the stupid language.

                               

                              As a side note, is there a way to see the SOAP that is being created and sent via the cfinvoke call, other than breaking out a packet sniffer?

                              • 12. Re: Webservice Driving me crazy!
                                Reed Powell Level 3

                                I spend my time programming in CF against the SOAP APIs for Google Adwords, Yahoo, and Microsoft adCenter, and this can drive you to drink.  The suggestion that you just roll your own XML and use CFHTTP is an excellant one.  Here are a couple of hints to making that happen.

                                 

                                First, download SoapUI from soapui.org and use it to view the request/response xml structures that you need to build.  All you do is feed it the WSDL URL(s) from the webservice(s). It is not perfect or all encompassing, but it is a good start.

                                 

                                Second, it sounds like there is a library of functioning PHP code for you to work with.  Load that up onto a webserver running PHP, and turn on SOAP logging and run some of their examples.  That will give you some real, known to work, SOAP documents for the requests, and examples of what might come back from the webservice.  Use those inside of CFXML to build the SOAP requests, use xmlParse() on the cfhttp.filecontent that comes back (after checking for http errors and xml faults), and you should be in business.

                                 

                                Good luck,

                                -reed

                                1 person found this helpful
                                • 13. Re: Webservice Driving me crazy!
                                  insuractive Level 3

                                  NOW WITH CODE COMMENTS. YAY.

                                   

                                  It sounds a lot scarier than it really is.  Many web services (mostly .NET based ones) include auto-documentation that include the SOAP xml so you can just copy and paste the XML into CF then swap in your values.  The basic idea is something like this:

                                   

                                  <!--- This code builds your SOAP XML --->

                                  <cfsavecontent variable="soapBody">
                                  <cfoutput>
                                  <?xml version="1.0" encoding="utf-8"?>

                                  <!-- SOAP Envelope Wrapper -->
                                  <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>

                                       <!-- Your Method Name (as XML entity) -->
                                      <MY_WEBSERVICE_METHOD xmlns="http://XML_NAMESPACE_FOR_WEB_SERVICE_METHODS_GOES_HERE/">

                                        <!-- Your complex XML data type argument -->
                                        <Web_Service_Argument_Name_GOES_HERE>

                                            <!-- build your XML according to the specification laid out in the WSDL  (should have the same structure and all required elements -->

                                            <ARGUMENT_SUB_ELEMENT>

                                                     <ARGUMENT_SUB_SUB_ELEMENT>

                                                      </ARGUMENT_SUB_SUB_ELEMENT>

                                            </ARGUMENT_SUB_ELEMENT>
                                        </Web_Service_Argument_Name_GOES_HERE>
                                      </GetRequestString>
                                    </soap:Body>
                                  </soap:Envelope>
                                  </cfoutput>
                                  </cfsavecontent>

                                   

                                  <!--- This code posts your SOAP XML to the web service --->

                                  <cfhttp url="WSDL_LOCATION_GOES_HERE" method="post" result="httpResponse">

                                  <!--- The SOAPAction is sent as a HTTP Header, not a Form field --->
                                  <cfhttpparam type="header" name="SOAPAction" value="""SOAP_ACTION(METHOD) FROM WSDL GOES HERE""" />

                                  <!--- Make sure server has compression turned off so that it doesn't mess with CFHTTP --->
                                  <cfhttpparam type="header" name="accept-encoding" value="no-compression" />

                                  <!--- Send your data --->
                                  <cfhttpparam type="xml" value="#trim( soapBody )#" />
                                  </cfhttp>

                                   

                                  I use SOAPSonar Personal 5 from Crosscheck Networks as a quick, free way to view/capture SOAP messages.  It does a lot of the heavy lifting for you interms of identifying the SOAP XML for those web services that don't already have sample messages to use as templates.

                                  1 person found this helpful
                                  • 14. Re: Webservice Driving me crazy!
                                    Avatar

                                    Go here :

                                     

                                    http://www.soapclient.com/soaptest.html

                                     

                                    enter in the WSDL, you will see the all of the service documentation for each method.

                                     

                                    Scroll down until you get to the sendDocument method.

                                     

                                    You can fill-in the info on this page and invoke the code to see what you get as a response.

                                     

                                    I have attempted to use your information you supplied but I believe I am stuck without the senderInfo credentials, so you will need to test it yourself.

                                     

                                    If you are not comfortable sending that info via the site, you can at least use the structured elements as the basis for your code.

                                    • 15. Re: Webservice Driving me crazy!
                                      insuractive Level 3

                                      Mega Bonus - you are lucky enough to have a web service that comes with outstanding documentation.  They include the Sample XML that you can use right in the documentation:

                                       

                                      https://secure.echosign.com/static/apiv9/apiMethods9.jsp

                                       

                                      Sample XML (create using <cfsavecontent> block):

                                      - Navigate to the method you are interested in and click on the "Request" link on the upper right.  Boom - your SOAP XML template is all ready for you to start populating with data.

                                       

                                       

                                      Extra CF Headers to use with <cfhttp>:

                                      https://secure.echosign.com/static/api/apiSoapHeaderInfo.jsp

                                       

                                      It looks like they actually use a SOAPAction of "", so don't include anything in that <cfhttpparam type="header"> entry.  (You may have to include an extra set of quotation marks though - try it both ways, see what works).

                                      • 16. Re: Webservice Driving me crazy!
                                        kenji776 Level 1

                                        Wow that is awesome! That was really cool of them to include that sample request. So now I am making the request and get back a bunch of... soap it looks like. What do I do with it? Here is what i am seeing.

                                         

                                        http://portal.fpitesters.com/echosigntestcfm.cfm

                                        • 17. Re: Webservice Driving me crazy!
                                          kenji776 Level 1

                                          Nevermind, got it. Thanks so much guys, I really really appreciate it.

                                          • 18. Re: Webservice Driving me crazy!
                                            TheRealAgentK Level 2

                                            Just an additional note. This article is a must-read when it comes to complex data structures and web services:

                                             

                                            http://tjordahl.blogspot.com/2008/04/reprint-consuming-web-service-complex.html

                                             

                                            Actually, Tom's complete blog really.

                                             

                                            Cheers

                                            Kai