My authorisation header looks like this (params altered slightly for security and line breaked for easier reading):
<cfset oAuthHeader = 'OAuth oauth_consumer_key="zz3u0Lf9XxkC2KX839r2MS0fDltvLquow3ZMLaOw",
oauth_nonce="9BD4FAE88D1B213F86D908FE183F0501C682EE2F",
oauth_signature="Zy91IhXWGcMxyuAVIlGX%2F3ULTWU%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1337169270",
oauth_version="1.0"'
My cfhttp call looks like this:
<cfhttp url="#oRequestReq.getNormalizedHttpURL()#" method="POST">
<cfhttpparam type="header" name="Authorization" value="#oAuthHeader#">
<cfloop collection="#arguments#" item="key">
<cfif key neq 'fieldnames'>
<cfhttpparam type="formfield" name="#key#" value="#arguments[key]#">
</cfif>
</cfloop>
</cfhttp>
Running <cfdump var="#GetHttpRequestData()#">, I get the following, which shows that my fields are passing through as formfield params OK, but my Authorization header is nowhere to be seen.
![enter image description here][1]
Shouldn't the Authorization header be included in the Headers struct?
[1]: http://i.stack.imgur.com/VbQQO.jpg
How are you getting the oauth_signature? It's not a hard-coded thing in OAuth - it's being generated each time.
I'd suggest using this library http://oauth.riaforge.org/
There are some examples there that should help you get started.
Shouldn't it be...
<cfset oAuthHeader = {
'oauth_consumer_key'="zz3u0Lf9XxkC2KX839r2MS0fDltvLquow3ZMLaOw",
'oauth_nonce'="9BD4FAE88D1B213F86D908FE183F0501C682EE2F",
'oauth_signature'="Zy91IhXWGcMxyuAVIlGX%2F3ULTWU%3D",
'oauth_signature_method'="HMAC-SHA1",
'oauth_timestamp'="1337169270",
'oauth_version'="1.0"
}>
<cfhttp url="#oRequestReq.getNormalizedHttpURL()#" method="POST">
<cfloop collection="#oAuthHeader#" item="key">
<cfhttpparam type="header" name="#key#" value="#oAuthHeader[key]#">
</cfloop>
<cfloop collection="#arguments#" item="key">
<cfif key neq 'fieldnames'>
<cfhttpparam type="formfield" name="#key#" value="#arguments[key]#">
</cfif>
</cfloop>
...
</cfloop>
?
Related
I am trying to loops through a json response in CFML, and need to do one call to get the number of pages, then a call for each successive page, then loop through items to get my data. In doing so I have a nested loop that requires a nested variable. So, for example, my initial loop will result in:
#jsonarray.items.1.id#
#jsonarray.items.2.id#
#jsonarray.items.3.id#
#jsonarray.items.4.id#
and so on.
So in my inside loop, I am trying to replace that number by doing another loop like this:
<cfloop from="1" to="50" index="thisBatchItem">
</cfloop>
But then, I would have to nest the index inside my variable, and that of course does not work.
Can someone point to what I am missing here?
Here is the code I am working with, and you will see the obvious places where I have the problem. I put in #jsonarray.items.#thisBatchItem#.id# just to show where I am trying to make this happen. I know it does not work.
<cfhttp method="GET" result="httpResp" url="https://example.com/api?filter[batch.date][gte]=2022-02-01&filter[batch.date][lte]=2022-03-07&page=1&per-page=50" throwonerror="false">
<cfhttpparam type="header" name="Authorization" value="Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX">
<cfhttpparam type="header" name="Content-type" value="application/json">
</cfhttp>
<cfoutput>
<cfloop from="1" to="#pageCount#" index="thisPage">
<cfhttp method="GET" result="httpResp" url="https://example.com/api?filter[batch.date][gte]=2022-02-01&filter[batch.date][lte]=2022-03-07&page=#thisPage#&per-page=50" throwonerror="false">
<cfhttpparam type="header" name="Authorization" value="Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX">
<cfhttpparam type="header" name="Content-type" value="application/json">
</cfhttp>
<cfset jsonarray = deserializeJson(httpResp.fileContent)>
<cfloop from="1" to="50" index="thisBatchItem">
<cfif StructKeyExists(jsonarray,"#jsonarray.items.#thisBatchItem#.id#")>
#jsonarray.items.[#thisBatchItem#].id#<br>
</cfif>
</cfloop>
</cfloop>
</cfoutput>
Since items is an array of structures, it's simpler and cleaner to use an "array" loop.
If some of the structure keys are optional, try using the safe navigation operator ?.. In the example below, it prevents an error from occurring when the code uses a non-existent key name like "lookMaNoErrors"?
<cfset jsonResponse = deserializeJson(httpResp.fileContent)>
<cfloop array="#jsonResponse.items#" index="currentItem">
id #currentItem.id#<br>
lookMaNoErrors #currentItem?.lookMaNoErrors#<br>
</cfloop>
Update:
To answer the original question, you need to use structure notation. Also, instead of hard coding the upper limit of the loop, use the array length instead:
<cfloop from="1" to="#arrayLen(jsonResponse.items)#" index="itemIndex">
id #jsonResponse.items[itemIndex].id#
</cfloop>
demo trycf.com
We have ColdFusion tasks defined like this:
<cfschedule
action="update"
task="Test"
operation="HTTPRequest"
url="#path.getFileFolder()#tasks/test.cfm"
startDate="#now()#"
startTime="00:00"
interval="daily"
resolveURL="no"
publish="yes"
file="test.txt"
path="#path.getLogFolder#"
eventHandler="tasks.eventHandler"
onException="invokeHandler">
The onError function in the eventHandler looks like this:
<cffunction name="onError" returntype="void">
<cfargument name="context" type="struct" required="false" />
<cfscript>
var slackHookURL = 'urlToOurSlackErrorChannel';
var slackMessage = 'ourSlackMessage';
</cfscript>
<cftry>
<cfhttp url="#slackHookURL#" method="post" result="httpResp" timeout="60">
<cfhttpparam type="header" name="Content-Type" value="application/json" />
<cfhttpparam type="body" value="#slackMessage#" />
</cfhttp>
<cfcatch></cfcatch>
</cftry>
</cffunction>
We had the problem that after a server switch our config file missed a / in the file folder path. So the url referenced in all of our tasks pointed to something like https://ourPagetasks/test.cfm instead of https://ourPage/tasks/test.cfm. The onError function hasn't been fired. We just "accidentally" stumbled upon all our tasks not having been executed ever since.
In the test.txt log file however we found the message "Connection timeout". Shouldn't the onError function warn us if that happens? Or is there any workaround so I can check the text that's about to be written to the log file? The onTaskEnd function of the eventHandler is only allowed to have the parameter context which tells me nothing about what's going to be logged.
I hope I explained my problem somehow understandable. Thanks in advance!
I managed to implement a workaround. In our scheduledTasks.cfm I added the following lines at the end to check if any of the urls are invalid:
<!--- Check if the tasks are defined correctly --->
<cfschedule action="list" mode="server" result="tasks" />
<cfloop query="tasks">
<cfhttp method="head" url="#tasks.URL#" />
<cfif len(cfhttp.errorDetail)>
<cfscript>
slackHookURL = 'urlToOurSlackErrorChannel';
slackMessage = 'ourSlackMessage';
</cfscript>
<cftry>
<cfhttp url="#slackHookURL#" method="post" result="httpResp" timeout="60">
<cfhttpparam type="header" name="Content-Type" value="application/json" />
<cfhttpparam type="body" value="#slackMessage#" />
</cfhttp>
<cfcatch></cfcatch>
</cftry>
</cfif>
</cfloop>
We're upgrading to Google's reCaptcha and it adds a field with name "g-recaptcha-response" to the form during submit. We them need to verify the response to Google with code like this:
<cfhttp url="https://www.google.com/recaptcha/api/siteverify" method="post" result="captchaResult">
<cfhttpparam type="formfield" name="secret" value="SECRET">
<cfhttpparam type="formfield" name="response" value="#Form.g-recaptcha-response#">
<cfhttpparam type="formfield" name="remoteip" value="#CGI.REMOTE_ADDR#">
</cfhttp>
However, the #Form.g-recaptcha-response# gives Element G is undefined in FORM.
Is there another way to reference Form scope to allow hyphens?
That will give you an error if the form variable doesn't exist. Try this instead:
<CFSET Form.Response = "">
<CFIF StructKeyExists(Form, "g-recaptcha-response")>
<CFSET Form.Response = form["g-recaptcha-response"]>
</CFIF>
I wrote a reCAPTCHA v2 UDF yesterday. I wanted to support sToken so I could use the same API key on multiple websites.
http://gist.github.com/JamoCA/c4e83ca402bd6175a1d7
Just found a solution:
<cfhttpparam type="formfield" name="response" value="#Form["g-recaptcha-response"]#">
I am trying to loop over a cookie coming from cfhttp but it is not displaying correct results.
Below is my code
<cfhttp url="#address#" method="get" throwOnError="Yes" resolveurl="false">
<cfset cookies = cfhttp.responseHeader['Set-Cookie'] />
<cfloop collection="#cookies#" item="k">
<cfset temp = REReplace(k, ";.*", "")>
<cfset cookieName = listfirst(temp,'=')>
<cfset cookievalue = listlast(temp,'=')>
<cfhttpparam type="cookie" name="#cookieName#" value="#cookievalue#">
</cfloop>
<cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
<cfhttpparam type="Header" name="TE" value="deflate;q=0">
</cfhttp>
Second attempt:
From one call, I am getting cookies and I am putting them in a structure as follows:
<cfset cookies = cfhttp.responseHeader['Set-Cookie'] />
<cfset cookieStruct=StructNew()>
<cfloop collection="#cookies#" item="key">
<cfset cookieKeyAndValue = REReplace(key, ";.*", "")>
<cfset cookieKey = listfirst(cookieKeyAndValue,'=')>
<cfset cookieValue = listlast(cookieKeyAndValue,'=')>
<cfset StructInsert(cookieStruct,cookieKey,cookieValue)>
</cfloop>
<cfdump var="#cookieStruct#" abort>
<cfhttp url="#addr#" method="get" throwOnError="Yes" resolveurl="false" result="objAddress">
<cfloop collection="#cookieStruct#" item="key">
<cfhttpparam type="cookie" name="#key#" value="#cookieStruct[key]#">
</cfloop>
<cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
<cfhttpparam type="Header" name="TE" value="deflate;q=0">
</cfhttp>
This is giving me an error:
Invalid collection ASPJGASGHSG=KBHFPN; path=/. Must be a valid structure or COM object. Loop error.
CFHTTP GET returns a result into the CFHTTP data structure. Having the loop inside the CFHTTP open/close tag results in trying to loop over something that does not exist yet.
<cfhttp url="#address#" method="get" throwOnError="Yes" resolveurl="false" >
<cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
<cfhttpparam type="Header" name="TE" value="deflate;q=0">
</cfhttp>
<cfset cookies = cfhttp.responseHeader['Set-Cookie'] />
<cfloop collection="#cookies#" item="k">
<cfset temp = REReplace(k, ";.*", "")>
<cfset cookieName = listfirst(temp,'=')>
<cfset cookievalue = listlast(temp,'=')>
</cfloop>
I'm not sure what you are attempting to do here but if there is more than one cookie you're going to need different code in the loop.
I am trying to post to twitter. I have the app already authenticated and now want to post an update.
This is what my http post is at:
<cfhttp url="http://api.twitter.com/1/statuses/update.json" method="post">
<cfhttpparam type="header" name="status" value="#urlEncodedFormat('my test post')#" />
<cfhttpparam type="header" name="oauth_consumer_key" value="xxx" />
<cfhttpparam type="header" name="oauth_nonce" value="xxx" />
<cfhttpparam type="header" name="oauth_signature_method" value="#urlEncodedFormat('HMAC-SHA1')#" />
<cfhttpparam type="header" name="oauth_token" value="xxx" />
<cfhttpparam type="header" name="oauth_timestamp" value="#GetTickCount()#" />
<cfhttpparam type="header" name="oauth_version" value="1.0" />
</cfhttp>
Has anyone done this? Am I going down the right route?
have you read this?
http://dev.twitter.com/pages/auth#auth-request
you need to construct the "signature base string" and post as body (warning: untested code, for CF8+)
<cffunction name="makeSignatureBaseString" returntype="string" output="false">
<cfargument name="httpMethod" type="string" required="true">
<cfargument name="baseUri" type="string" required="true">
<cfargument name="values" type="struct" required="true">
<cfset var signatureBaseString = "#httpMethod#&#URLEncodedFormat(baseUri)#&">
<cfset var keys = StructKeyArray(values)>
<cfset var key = "">
<cfset ArraySort(keys, "textNoCase")>
<cfloop array="#keys#" index="key">
<cfset signatureBaseString &= URLEncodedFormat("&#key#=#values[key]#")>
</cfloop>
<cfreturn signatureBaseString>
</cffunction>
-
<!--- using values from http://dev.twitter.com/pages/auth#auth-request --->
<cfset params = {
oauth_consumer_key = "GDdmIQH6jhtmLUypg82gる",
oauth_nonce = "oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y",
oauth_signature_method = "HMAC-SHA1",
oauth_token = "819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw",
oauth_timestamp = "1272325550",
oauth_version = "1.0"
}>
<cfhttp url="http://api.twitter.com/1/statuses/update.json" method="POST">
<cfloop collection="#params#" item="key">
<cfheader type="header" name="#key#" value="#params[key]#">
</cfloop>
<!--- add status to the params for makeSignatureBaseString() --->
<cfset params.status = "setting up my twitter 私のさえずりを設定する">
<cfhttpparam type="body"
value="#makeSignatureBaseString('POST', 'http://api.twitter.com/1/statuses/update.json', params)#">
</cfhttp>