Error when using CFHTTP - coldfusion

I have built an app on openBD using CFML. In the app I am using CFHTTP like following:
<cfcomponent output="false">
<cfprocessingdirective pageencoding="utf-8">
<cfset setEncoding("URL", "utf-8")>
<cffunction name="search" access="remote" returnType="any" returnFormat="plain">
<cfargument name="q" type="string" default="" required="yes">
<cfargument name="rows" type="string" default="&rows=120" required="yes">
<cfargument name="score" type="string" default="&sort=score%20desc" required="yes">
<cfargument name="highlight" type="string" default="&hl=true" required="yes">
<cfargument name="json" type="string" default="&wt=json" required="yes">
<cfargument name="phrasehighlighter" type="string" default="&hl.usePhraseHighlighter=true" required="yes">
<cfargument name="filtr" type="string" default="&fq=model:*" required="yes">
<cfargument name="callbackP" type="string" required="false">
<cfset theUrl = 'http://localhost:8090/solr/ktimatologio/select/?hl.requireFieldMatch=true&hl.fl=*&q=' & #Arguments.q# & #ARGUMENTS.rows# & #ARGUMENTS.score# & #ARGUMENTS.highlight# & #ARGUMENTS.json# & #ARGUMENTS.phrasehighlighter#>
<cfhttp url= "#theUrl#" result="rs"></cfhttp>
…………………
…………………
…………………
…………………
</cfcomponent>
When I run it I am getting the error: 'Failed to set URL: Invalid query'.
I am damn stuck! What does this error means? I think on Adobe’s CFML engine is working properly, but I am not sure. My "programing" quiver was run out of arrows!.I need to get this working on openBD.
With respect,
Tom
Greece

You should have a method argument (either GET or POST, and you should remove the port. Add the port as the port attribute to the tag, like so:
<cfset theUrl = 'http://localhost:8090/solr/ktimatologio...' />
<cfhttp method="get" url="#theURL#" port="8090" result="rs>
You'll also be better off adding all of those querystring values as cfhttpparam tags instead of appending them on your URL, like this:
<cfset theUrl = 'http://localhost/solr/ktimatologio/select/>
<cfhttp method="get" url="#theURL#" port="8090" result="rs>
<cfhttpparam type="URL" name="q" value="#Arguments.q#" />
<cfhttpparam type="URL" name="wt" value="#Arguments.JSON" />
.... more params ....
</cfhttp>
I would also highly recommend you remove the querystring name from the arguments. If you decide you want to change your implementation, you're going to be in trouble... Just accept the value you want to supply for each querystring value, but not the name of the value itself.

Related

calling a init method of component b from a

In one of my components, abc.cfc, I am extending another one: xyz.cfc. Component xyz.cfc has an init() method which is expecting: datasource,username,password.
In my application, I am using it like this:
<cfset this.mappings = structNew() />
<cfset this.mappings["/com"] = getDirectoryFromPath(getCurrentTemplatePath()) & "com/">
<cfset Application.tools = new com.abc()>
Now in abc.cfc I am doing the following:
<cfcomponent hint="The File which acces the Information about the Detail" extends="xyz">
and xyz.cfc has the following function:
<cffunction name="init" access="public" output="No" returntype="mysql" hint="Initializes the component">
<cfargument name="datasource" required="Yes" type="string" />
<cfargument name="username" required="Yes" type="string" />
<cfargument name="password" required="Yes" type="string" />
<cfscript>
variables.instance = structNew();
setDatasource(argumentcollection=arguments); // set datasource information
clearCache(); // create cache struct
variables.instance.trim = true;
return this;
</cfscript>
</cffunction>
It is producing an error like this:
The DATASOURCE parameter to the INIT function is required but was not passed in. The error occurred in C:/ColdFusion11/cfusion/wwwroot/project1/admin/Application.cfc: line 28
You just pass the argument values in the constructor call:
<cfset Application.tools = new com.abc(datasource=datasource, etc)>

Retrieve email and phone number via Google API

I am working with Google API using Coldfusion, So making a call to the following URL:
https://www.googleapis.com/plus/v1/people/{userID}?key={MyGoogleKey}
I am able to get the details of the user. Whatever they have shared in their google plus account. One thing that is missing is their email address and phone numbers (if available).
I think, I need to make another call to the API to get the email and phone numbers, but I am struggling with how to do that. Here is the code I am trying to use:
<cfset objGoogPlus = createObject("component","services.auth").init(apikey="#application.google_server_key#",parseResults=true)>
<cfdump var="#objGoogPlus.people_get(userID='#data.id#')#">
<cffunction name="init" access="public" output="false" hint="I am the constructor method.">
<cfargument name="apiKey" required="true" type="string" hint="I am the application's API key to access the services." />
<cfargument name="parseResults" required="false" type="boolean" default="false" hint="A boolean value to determine if the output data is parsed or returned as a string" />
<cfset variables.instance.apikey = arguments.apiKey />
<cfset variables.instance.parseResults = arguments.parseResults />
<cfset variables.instance.endpoint = 'https://www.googleapis.com/plus/v1/' />
<cfreturn this />
</cffunction>
<cffunction name="getparseResults" access="package" output="false" hint="I return the parseresults boolean value.">
<cfreturn variables.instance.parseResults />
</cffunction>
<cffunction name="people_get" access="public" output="false" hint="I get a person's profile.">
<cfargument name="userID" required="true" type="string" hint="The ID of the person to get the profile for. The special value 'me' can be used to indicate the authenticated user." />
<cfargument name="parseResults" required="false" type="boolean" default="#getparseResults()#" hint="A boolean value to determine if the output data is parsed or returned as a string" />
<cfset var strRequest = variables.instance.endpoint & 'people/' & arguments.userID & '?key=' & variables.instance.apikey />
<cfreturn getRequest(URLResource=strRequest, parseResults=arguments.parseResults) />
</cffunction>
<cffunction name="getRequest" access="private" output="false" hint="I make the GET request to the API.">
<cfargument name="URLResource" required="true" type="string" hint="I am the URL to which the request is made." />
<cfargument name="parseResults" required="true" type="boolean" hint="A boolean value to determine if the output data is parsed or returned as a string" />
<cfset var cfhttp = '' />
<cfhttp url="#arguments.URLResource#" method="get" />
<cfreturn handleReturnFormat(data=cfhttp.FileContent, parseResults=arguments.parseResults) />
</cffunction>
<cffunction name="handleReturnFormat" access="private" output="false" hint="I handle how the data is returned based upon the provided format">
<cfargument name="data" required="true" type="string" hint="The data returned from the API." />
<cfargument name="parseResults" required="true" type="boolean" hint="A boolean value to determine if the output data is parsed or returned as a string" />
<cfif arguments.parseResults>
<cfreturn DeserializeJSON(arguments.data) />
<cfelse>
<cfreturn serializeJSON(DeserializeJSON(arguments.data)) />
</cfif>
</cffunction>
<cffunction access="public" name="getProfileEmail" returntype="any" returnformat="json">
<cfargument name="accesstoken" default="" required="yes" type="any">
<cfhttp url="googleapis.com/oauth2/v1/userinfo"; method="get" resolveurl="yes" result="httpResult">
<cfhttpparam type="header" name="Authorization" value="OAuth #arguments.accesstoken#">
<cfhttpparam type="header" name="GData-Version" value="3">
</cfhttp>
<cfreturn DeserializeJSON(httpResult.filecontent.toString())>
</cffunction>
I do not what to say, but i used the following method and its seems to bring the email-address, not with new way but with old way:
<cfset googleLogin = objLogin.initiate_login(
loginUrlBase = "https://accounts.google.com/o/oauth2/auth",
loginClientID = application.google_client_id,
loginRedirectURI = application.google_redirecturl,
loginScope = "https://www.googleapis.com/auth/userinfo.email"
)>
I discuss Google+ and Sign In via this blog post (http://www.raymondcamden.com/index.cfm/2014/2/20/Google-SignIn-and-ColdFusion), and yes, I know it is bad to just point to a blog post so none of my hard work there will be appreciated by SO (sigh). As Prisoner said, what you get is based on the scopes initially requested. Here are the docs on additional scopes: https://developers.google.com/+/api/oauth#login-scopes. Email is there, but not phone as far as I see. And to be honest, I don't see a place to enter my phone # at Google+ anyway.
Based on the "getProfileEmail" function that you've added, it looks like you're using the userinfo scope and access point. Google has announced that this method is deprecated and will be removed in September 2014.
You should switch to using the people.get method and adding the email scope to the G+ Sign-in button that Raymond has outlined on his blog.
Update
In your call to objLogin.initiate_login you should change the loginScope parameter to use both the profile and email scopes at a minimum (see https://developers.google.com/+/api/oauth#scopes for a more complete list of scopes you may wish to use). So it might look something like:
<cfset googleLogin = objLogin.initiate_login(
loginUrlBase = "https://accounts.google.com/o/oauth2/auth",
loginClientID = application.google_client_id,
loginRedirectURI = application.google_redirecturl,
loginScope = "profile email"
)>
You can then use the people.get API call to get the information you need. Looking at your getRequest function, however, this appears to call people.get using the API Key, which is insufficient to get email - all it can do is get public information. You need to call people.get in the same way you called userinfo in the getProfileEmail function. I don't know ColdFusion well enough, but you should be able to adapt this function to call the people.get endpoint instead.

Upload Files To Google Drive Using ColdFusion

*NEW UPDATED FOR BETTER SECOND PART - NOW GETS TO "308 Resume Incomplete", even though file should be just one upload!
I am using the foundation of cfgoogle from Ray Camden. But Google has deprecated the code for document uploads. The new standard is Resumable Media Uploads.
I have this part working (up to and including the "Initiating a resumable upload request") in the above referenced Google document.
Calling Page:
<cfset application.cfc.Google = createObject('component','#path_cf_cfc#Google') />
<cfset application.cfc.GoogleDocs = createObject('component','#path_cf_cfc#GoogleDocs') />
<cfset gtoken = application.cfc.GoogleDocs.authenticate(emailaddress,password)>
<CFSET testdoc = "a\filepath\documentname.doc">
<CFSET FileType = "application/msword">
<CFSET FileTitle = "test_001">
<cfset temp = application.cfc.GoogleDocs.upload_auth("#Application.Map.DocStorage##tv.testdoc#",FileType,FileTitle)>
<CFSET uploadpath = Listgetat(Listgetat(temp.header,ListContains(temp.header,"https://docs.google.com","#chr(10)#"),"#chr(10)#"),2," ") >
<cfset temp2 = application.cfc.GoogleDocs.upload_file("#Application.Map.DocStorage##tv.testdoc#",FileType,FileTitle,uploadpath)>
The code works up to and including the cfset temp line (getting the unique upload URI)
Here is the code for upload_auth:
<cffunction name="upload_auth" access="public" returnType="any" hint="I get a uniqu URI from Google API." output="false">
<cfargument name="myFile" type="string" required="true" hint="filepath to upload.">
<cfargument name="myType" type="string" required="true" hint="application/msword">
<cfargument name="myTitle" type="string" required="true" hint="name of doc">
<cfset GoogleUrl = "https://docs.google.com/feeds/upload/create-session/default/private/full">
<cfset GoogleVersion = 3>
<cfset FileSize = createObject("java","java.io.File").init(myFile).length()>
<cfhttp url="#GoogleUrl#" method="post" result="diditwork" resolveurl="no">
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#getAuth(variables.docservice)#">
<cfhttpparam type="header" name="GData-Version" value="#GoogleVersion#">
<cfhttpparam type="header" name="Content-Length" value="0">
<cfhttpparam type="header" name="X-Upload-Content-Type" value="#myType#">
<cfhttpparam type="header" name="X-Upload-Content-Length" value="#FileSize#">
<cfhttpparam type="header" name="Slug" value="#myTitle#">
</cfhttp>
<cfreturn diditwork>
</cffunction>
OK - So Far So Good.
But here is where it breaks down:
Running upload_file returns "308 Resume Incomplete" (A lest it's not a 400!) from Google. Arrgh!!
Here is the upload_file -
<cffunction name="upload_file" access="public" returnType="any" hint="I upload the document." output="false">
<cfargument name="myFile" type="string" required="true" hint="filepath to upload.">
<cfargument name="myType" type="string" required="true" hint="like application/msword">
<cfargument name="myTitle" type="string" required="true" hint="name of doc">
<cfargument name="myAuthPath" type="string" required="true" hint="call auth">
<cfset FileSize = GetFileInfo(myFile).size >
<CFSET tv.tostartwithzero = FileSize - 1>
<CFFILE action="read" file="#myfile#" variable="FileText">
<cfhttp url="#myAuthPath#" method="put" result="diditwork" resolveurl="no" multipart="yes" charset="utf-8" >
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#getAuth(variables.docservice)#">
<cfhttpparam type="header" name="GData-Version" value="#variables.GoogleVersion#">
<cfhttpparam type="header" name="Content-Length" value="#FileSize#">
<cfhttpparam type="header" name="Content-Range" value="bytes 0-#tv.tostartwithzero#/#FileSize#">
<cfhttpparam type="header" name="Content-Type" value="#myType#">
<cfhttpparam type="body" value="#trim(FileText)#">
</cfhttp>
<cfreturn diditwork>
</cffunction>
So, there we have it - where I am stuck.
I can get the unique URI, but (maybe because it is late at night) I'm brain dead on what I am doing wrong otherwise to complete the file upload.
All help is appreciated.
I strongly recommend taking a different approach here. There are two big issues with the path you're taking:
It appears the code is using the deprecated client login auth mechanism and is using the username/password instead of OAuth.
Using the deprecated documents list API instead of the newer Drive API.
Since you can call java, I'd suggest writing the upload code in plain Java then invoking it from your CF pages as needed.

Using CFFILE in a CFC Nightmare

I know this has been asked, but I cannot seem to find the solution that works.
I have a CFM page that uses the following to pass data to a CFC
<cfinvoke
component="common.cfcs.geotrails"
method="UpdateGeoTrail">
<cfinvokeargument name="title" value="#form.title#"/>
<cfinvokeargument name="Description_short" value="#form.Description_short#"/>
<cfinvokeargument name="Description" value="#form.description#"/>
<cfinvokeargument name="GTID" value="#form.gtid#"/>
<cfinvokeargument name="CatID" value="#form.catid#"/>
<cfif structKeyExists(form,"fileUpload") and len(form.fileUpload)>
<cfinvokeargument name="fileUpload" value="#form.fileUpload#"/>
</cfif>
</cfinvoke>
In the CFC that receives the data, I followed the direction at the Adobe Cookbook
<cffunction name="UpdateGeoTrail" access="public" returntype="void">
<cfargument name="title" type="string" required="yes">
<cfargument name="Description_short" type="string" required="yes">
<cfargument name="Description" type="string" required="yes">
<cfargument name="GTID" type="numeric" required="yes">
<cfargument name="CatID" type="numeric" required="yes">
<cfargument name="fileUpload" type="string" required="no">
<!--- IF THE IMAGE HAS BEEN UPLOADED --->
<!--- set the full path to the images folder --->
<cfif isdefined("arguments.fileUpload") AND len(arguments.fileUpload)>
<cfset tempmediapath = "#expandPath('/')#media/gtimages/temp/">
<cfset mediapath = "#expandPath('/')#media/gtimages/">
<cfset var cffile = "">
<cffile action="upload"
filefield="#ARGUMENTS.fileUpload#"
destination="#TempMediaPath#"
nameconflict="makeunique">
...
But I still get the dreaded error message...
"The form field /data/disk01/opt/coldfusion9/runtime/servers/coldfusion/SERVER-INF/temp/wwwroot-tmp/neotmp5003883285207133802.tmp did not contain a file."
If I follow the directions at StackExchange
( CFFILE - Uploading a file using a component )
<cffile action="upload"
filefield="fileUpload"
destination="#TempMediaPath#"
nameconflict="makeunique">
It passes without error, but a <CFDUMP> shows: [empty string].
What am I missing.
Thanks.
Phil
I know it isn't part of your cfc but did you make sure that the form has the enctype set?
<cfform action="/upload.cfm" enctype="multipart/form-data">
By removing the cffile scope, I was able to get it to work.

consuming coldfusion webservices

I am trying to build a web service. Here is my code for a simple web service which returns a string.
At the beginning i inserted some code from ben nadel
It refreshes the stubfile automatically because otherwise you get errors while passing parameters.
<cfcomponent
displayname="BaseWebService"
output = "false"
hint="This handles core web service features">
<cffunction
name="Init"
access="public"
returntype="any"
output="false"
hint="Returns an initialized web service instance.">
<cfreturn THIS />
</cffunction>
<cffunction
name="RebuildStubFile"
access="remote"
returntype="void"
output="false"
hint="Rebuilds the WSDL file at the given url.">
<cfargument name="Password" type="string" required="true" default="" />
<cfif NOT Compare(ARGUMENTS.Password, "sweetlegs!")>
<cfset CreateObject("java", "coldfusion.server.ServiceFactory"
).XmlRpcService.RefreshWebService(
GetPageContext().GetRequest().GetRequestUrl().Append("?wsdl").ToString()) />
</cfif>
<cfreturn />
</cffunction>
<cffunction
name="easyService"
access="remote"
returntype="any"
output="false">
<cfargument name="anyOutput" type="string" default="this and that" />
<cfargument name="xtype" type="string" required="yes" default="1" />
<cfif Compare(xtype, "1") EQ 0>
<cfset anyVar = "one" />
<cfelse>
<cfset anyVar = "two" />
</cfif>
<cfreturn anyVar>
</cffunction>
</cfcomponent>
Here I am trying to invoke the webservice.
<cfinvoke
webservice="https://[...]/Components/Webservice.cfc?wsdl"
method="RebuildStubFile">
<cfinvokeargument
name="Password"
value="sweetlegs!" />
</cfinvoke>
<cfinvoke
webservice="[...]/Components/Webservice.cfc?wsdl"
method="easyService"
returnVariable="anyVar" >
<cfinvokeargument
name="xtype"
value="2"
omit="true">
</cfinvoke>
<cfdump var="#anyVar#">
The first method of my web service component can be invoked but the second one always returns this error message:
coldfusion.xml.rpc.ServiceProxy$ServiceMethodNotFoundException: Web service operation easyService with parameters {xtype={2}} cannot be found.
at coldfusion.xml.rpc.ServiceProxy.invoke(ServiceProxy.java:149)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2301)
at coldfusion.tagext.lang.InvokeTag.doEndTag(InvokeTag.java:454)
If i type in the url of the webservice, by adding
?method=easyService&xtype=2
it returns the right value. but this is like passing values with a GET method.
i have been searching for hours and don't know where the problem occures.
I think when using WebService call you need to specify all arguments and use omit="true" on the proper one (not on xtype).
<cfinvoke
webservice="[...]/Components/Webservice.cfc?wsdl"
method="easyService"
returnVariable="anyVar" >
<cfinvokeargument
name="anyOutput"
value=""
omit="true">
<cfinvokeargument
name="xtype"
value="2">
</cfinvoke>