I'm working on my Application.cfc page for my SPA. So far I was able to create simple test and there is one problem that I notice. Flag that I set when user is loggedin always indicates to true. Here is my Application.cf page example:
<cffunction name="onApplicationStart" access="public" returntype="boolean" output="false">
<cfset APPLICATION.appStarted = now()>
<cfset APPLICATION.appCode = 'SPA'>
<cfset APPLICATION.title = "SPA Application">
<cfset APPLICATION.errorEmails = "myemail#gmail.com">
<cfset APPLICATION.CryptWord = randomSequence()>
<cfset APPLICATION.AppStarted = true>
<cfreturn true>
</cffunction>
<!--- Runs when your session starts --->
<cffunction name="OnSessionStart" access="public" returntype="void" output="false">
<!--- Clear the session. --->
<cfset StructClear( SESSION ) />
<cfset SESSION.loggedin = false>
<cfreturn />
</cffunction>
<!--- Run before the request is processed --->
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cfset var page = listLast(arguments.thePage,"/")>
<cfif !listFindNoCase("Login.cfm,Login.cfc",page)>
<cfif !structKeyExists(session, "loggedin") OR session.loggedin EQ false>
<cflocation url="Login.cfm" addToken="false">
</cfif>
</cfif>
<cfreturn true>
</cffunction>
As you can see above I first set some application variables in onApplicationStart() the onSessionStart() first I clear SESSION structure then set flag to false. Here is example of my Login.cfc:
<cfcomponent>
<cffunction name="checkLogin" access="remote" output="false" returnType="boolean">
<cfargument name="username" type="string" required="true">
<cfargument name="password" type="string" required="true">
<cfif arguments.username EQ "test" AND arguments.password EQ "test">
<cfset SESSION.loggedin = true>
<cfreturn true>
<cfelse>
<cfreturn false>
</cfif>
</cffunction>
</cfcomponent>
Then I use cfdump to check my SESSION variable on my Login.cfm (only has HTML form elements and JQuery function to submit the form). Doesn't matter if checkLogin function returns true or false value in loggedin variable is still equal to true. If anyone can see where I'm doing something wrong in my code please let me know. Thank you!
I have to create a web service in ColdFusion. I have tried the below 2 ways. Can anyone help me to find which one is the best way (Both Performance and security enhancement basis)
First Way
Created a cfm page like below;
<cfset result = StructNew() />
<cfset resultStruct = StructNew() />
<cfset validStruct = StructNew() />
<cfset VARIABLES.Sample = CreateObject("component","main.webservice.Sample")>
<cfif NOT isDefined("URL.method")>
<cfset result['status'] = false >
<cfset result['message'] = 'method is missing' />
<cfoutput>#SerializeJSON(result)#</cfoutput>
<cfabort>
</cfif>
<cfswitch expression="#URL.method#">
<cfcase value="get">
<cfset fieldList = "name">
<cfset validStruct = validate(fieldList) />
<cfif validStruct['status']>
<cfset resultStruct = VARIABLES.Sample.get(argumentCollection=URL) />
</cfif>
<cfoutput>#SerializeJSON(resultStruct)#</cfoutput>
<cfbreak>
</cfcase>
<cfcase value="put">
<cfset fieldList = "name,value">
<cfset validStruct = validate(fieldList) />
<cfif validStruct['status']>
<cfset resultStruct = VARIABLES.Sample.put(argumentCollection=URL) />
</cfif>
<cfoutput>#SerializeJSON(resultStruct)#</cfoutput>
<cfbreak>
</cfcase>
<cfdefaultcase>
<cfset result['status'] = false >
<cfset result['message'] = 'Not a valid method' />
<cfoutput>#SerializeJSON(result)#</cfoutput>
<cfbreak>
</cfdefaultcase>
</cfswitch>
And Created a cfc named 'Sample' under webservice folder and called like above.
WebService URL
http://test.com/webservice/Sample.cfm?method=get&name=test
Second Way
Called directly from the CFC Sample
Sample.CFC
<cfcomponent displayname="Sample" hint="Sample WebService" output="false">
<cffunction name="get" access="remote" returntype="struct" returnformat="json">
<cfargument name="name" required="true" type="string" >
<cfreturn StructNew() />
</cffunction>
<cffunction name="put" access="remote" returntype="struct" returnformat="json">
<cfargument name="name" required="true" type="string" >
<cfargument name="value" required="true" type="string" >
<cfreturn StructNew() />
</cffunction>
</cfcomponent>
WebService URL
http://test.com/webservice/Sample.CFC?method=get&name=test
The second method is the standard way to do WebServices in CFML. Along with the functionality, you are seeking you get standards based WSDL returns and definitions. It's a case of rebuilding the wheel. I'm sure the underlying CF code for ws could be optimized, but it's pretty good as is and has been field-tested by millions.
I would suggest setting up RESTful web services in ColdFusion. Here is an excellent article to get you started.
There's also Taffy which claims to make it simpler, although I have not used it.
Problem:
I have 2 files in the same folder:
index.cfm
application.cfc
My index.cfm is empty and below is my application.cfc code:
<cfcomponent>
<cffunction name="onApplicationStart">
<cfapplication name="TimeHistory" clientmanagement="No" sessionmanagement="No" setclientcookies="Yes" />
<cflock scope="application" type="exclusive" timeout=10>
<cfset application.test = "xxxxxxxxxxxxxx">
</cflock>
<cfreturn true />
</cffunction>
<cffunction name="onRequestStart">
<cfdump var="#application#" label="before">
<cfif structKeyExists(URL, "restart")>
<cfset onApplicationStart()>
</cfif>
<cfdump var="#application#" label="after">
<cfabort>
</cffunction>
</cfcomponent>
As you can see, I am setting a test variable in the application scope.
When I call the page with the ?restart URL query param, this is the output:
url: ../index.cfm?restart
Next,I removed the ?reset URL query param and loaded the page, this is the output:
url: ../index.cfm
Question:
Should it not have set the variable as part of the first call and then be persisted for future calls? How can I make it do so?
Based on +Leigh's comment , I modified my code which now works. Leaving this here as a solution. Thank you Leigh :)
<cfcomponent>
<cfscript>
this.name = "TimeHistory";
this.clientmanagement= "no";
this.sessionmanagement = "no";
this.setClientCookies = "no";
this.setDomainCookies = "no";
</cfscript>
<cffunction name="onApplicationStart">
<cflock scope="application" type="exclusive" timeout=10>
<cfset application.test = "xxxxxxxxxxxxxx">
</cflock>
<cfreturn true />
</cffunction>
<cffunction name="onRequestStart">
<cfdump var="#application#" label="before">
<cfif structKeyExists(URL, "restart")>
<cfset onApplicationStart()>
</cfif>
<cfdump var="#application#" label="after">
<cfabort>
</cffunction>
</cfcomponent>
My function calls the SendGrid API. It returns an Array + structure. I'm writing a function to return a CFQuery dataset.
Goal
I want to pass a deserialized data object to my function and get a query dataset.
Here is my working code and the output:
<cfparam name="variables.ddata" default="#structnew()#">
<!--- API Call Code here --->
<cfset arr = DESerializeJSON(returnStruct.Filecontent) />
<cfdump var="#arr#">
My code:
<cfset arrayit(arrobj= arr) >
<cfdump var="#variables.ddata#" >
<cffunction name="arrayit" access="public" returntype="void">
<cfargument name="arrobj" type="array" required="yes">
<cfset var arr=arguments.arrobj />
<cfloop from="1" to = "#arrayLen(arr)#" index="i">
<cfif isValid("string", arr[i])>
<cfset StructInsert(variables.ddata, i, arr[i]) />
</cfif>
<cfif isstruct(arr[i])>
<cfset structit(structobj = arr[i]) />
</cfif>
</cfloop>
</cffunction>
<cffunction name="structit" access="public" returntype="void" output="yes">
<cfargument name="structobj" type="any" required="yes">
<cfset stru = arguments.structobj />
<cfloop collection="#stru#" item="S">
<cfif isValid("string", stru[S])>
<cfset StructInsert( variables.ddata, S, stru[S]) />
</cfif>
<cfif isarray(stru[S])>
<cfset arrayit(arrobj = stru[S]) >
</cfif>
</cfloop>
</cffunction>
Result:
When I add this line in my function
<cfif isstruct(stru[S])>
<cfset variables.ddata = arrayit(arrobj = stru[S]) />
</cfif>
An error occurs:
Element type is undefined in a CFML structure referenced as part of an
expression. The error occurred on line 71.
** Full Code**
<cfsavecontent variable="returnStruct.Filecontent">
[{"date":"2016-04-05","stats":[{"type":"category","name":"5","metrics":{"blocks":1,"bounce_drops":0,"bounces":9,"clicks":4,"deferred":1,"delivered":1,"invalid_emails":8,"opens":4,"processed":1,"requests":1,"spam_report_drops":0,"spam_reports":1,"unique_clicks":3,"unique_opens":3,"unsubscribe_drops":0,"unsubscribes":9}}]}]
</cfsavecontent>
<cfset arr = DESerializeJSON(returnStruct.Filecontent) />
<cfloop from="1" to="#arrayLen(arr)#" index="i">
<cfif isValid("string", arr[i])>
<cfset StructInsert(variables.ddata, i, arr[i],true ) />
</cfif>
<cfif isstruct(arr[i])>
<cfsavecontent variable="rr">
<cfdump var="#arr[i]#" label="Line 48 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 48") />
<cfset structit(structobj = arr[i]) />
</cfif>
<cfif isarray(arr[i])>
<cfsavecontent variable="rr">
<cfdump var="#arr[i]#" label="Line 54 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 54") />
<cfset arrayit(arrobj = arr[i]) >
</cfif>
</cfloop>
</cffunction>
<cffunction name = "structit" access="public" returntype="void" output="yes">
<cfargument name = "structobj" type="any" required="yes">
<cfset stru = arguments.structobj />
<cfloop collection="#stru#" item="S">
<cfif isValid("string", stru[S])>
<cfset StructInsert( variables.ddata, S, stru[S],true) />
</cfif>
<cfif isarray(stru[S])>
<cfsavecontent variable="rr">
<cfdump var="#stru[S]#" label="Line 86 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 87") />
<cfset arrayit(arrobj = stru[S]) >
</cfif>
<cfif isstruct(stru[S])>
<cfsavecontent variable="rr">
<cfdump var="#stru[S]#" label="Line 97 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 97") />
<cfset structit(structobj = stru[S]) />
</cfif>
</cfloop>
</cffunction>
ERROR
Your UDF arrayit accepts an argument of type array but when that condition is true then a struct is being passed so, the error.
i.e.,
<cfif isStruct(stru[S])>
<!--- This means stru[S] is a struct --->
<cfset variables.ddata = arrayit(arrobj = stru[S])>
<!--- arrObj should be of type 'array' --->
</cfif>
So, it should be:
<cfif isStruct(stru[S])>
<cfset variables.ddata = structit(structobj = stru[S])>
</cfif>
But, the error for this case will be different than that you have added.
Additionally,
StructInsert() takes an optional argument allowoverwrite which is by default false and according to docs:
if key exists and allowoverwrite = "False", ColdFusion throws an
exception.
I just did it! :) just wanted to share my project with you guys also hope it will help someone else also...
Request if you guys find anything you feel I can improve please share.
Special Thanks for response on my post. #Beginner & #Leigh
API Call Json Return: 1
<cfsavecontent variable="returnStruct.Filecontent">
[{"date":"2016-04-05","stats":[{"type":"category","name":"5","metrics":{"blocks":1,"bounce_drops":0,"bounces":9,"clicks":4,"deferred":1,"delivered":1,"invalid_emails":8,"opens":4,"processed":1,"requests":1,"spam_report_drops":0,"spam_reports":1,"unique_clicks":3,"unique_opens":3,"unsubscribe_drops":0,"unsubscribes":9}}]}]
</cfsavecontent>
<cfset arr = DESerializeJSON(returnStruct.Filecontent) />
CFC : 2
<cfcomponent>
<cfparam name="variables.qryclsvar" default="" type="any"/>
<cfparam name="variables.qryclsvarfg" default="true" type="any"/>
<cffunction name="APItoquery" access="public" returntype="any">
<cfargument name = "APIobj" type="any" required="yes">
<cfset var vAPIobj = arguments.APIobj />
<cfset var APIDATA = structnew() />
<cfset var APIDATAqr = "" />
<cftry>
<cfloop from="1" to="#arrayLen(vAPIobj)#" index="jj">
<cfif isarray(vAPIobj[jj])>
<cfset APIDATA = arrayit(structobj = vAPIobj[jj] ,datastruct = APIDATA) />
</cfif>
<cfif isstruct(vAPIobj[jj])>
<cfset APIDATA = structit(structobj = vAPIobj[jj],datastruct = APIDATA) />
</cfif>
<cfif NOT StructIsEmpty(APIDATA)>
<!--- Add in query object --->
<cfset APIDATAqr = structtoquery(structobj= APIDATA) />
<cfelse>
<cfset APIDATAqr ="NO Data Found!" />
</cfif>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="APItoquery">
</cfcatch>
</cftry>
<cfreturn APIDATAqr>
</cffunction>
<cffunction name = "arrayit" access="public" returntype="any">
<cfargument name = "arrobj" type="any" required="yes">
<cfargument name = "datastruct" type="any" required="yes" >
<cfset var arr = arguments.arrobj />
<cfset var arrdata = arguments.datastruct />
<cftry>
<cfloop from="1" to="#arrayLen(arr)#" index="i">
<cfif ArrayContains(arr, i) >
<cfset StructInsert(arrdata, i, arr[i],true ) />
</cfif>
<cfif isarray(arr[i])>
<cfset arrdata = arrayit(arrobj = arr[i] ,datastruct = arrdata) >
</cfif>
<cfif isstruct(arr[i]) >
<cfset stdata = structit(structobj = arr[i],datastruct = arrdata) />
</cfif>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="arrayit">
</cfcatch>
</cftry>
<cfreturn arrdata>
</cffunction>
<cffunction name = "structit" access="public" returntype="any" output="yes">
<cfargument name = "structobj" type="any" required="yes">
<cfargument name = "datastruct" type="any" required="yes">
<cfset var stru = arguments.structobj />
<cfset var stdata = arguments.datastruct />
<cftry>
<cfloop collection="#stru#" item="S">
<cfif isarray(stru[S])>
<cfset stdata = arrayit(arrobj = stru[S] ,datastruct = stdata) >
<cfelseif isstruct(stru[S]) >
<cfset stdata = structit(structobj = stru[S],datastruct = stdata) />
<cfelse>
<cfset StructInsert( stdata, S, stru[S],true) />
</cfif>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="structit">
</cfcatch>
</cftry>
<cfreturn stdata>
</cffunction>
<cffunction name = "structtoquery" access="public" returntype="any" output="yes">
<cfargument name = "structobj" type="any" required="yes">
<cfset var vstructobj = arguments.structobj />
<cfset var cols = StructKeyList(vstructobj)>
<cfset var colstyp = "">
<cftry>
<cfif variables.qryclsvarfg EQ true>
<cfloop from="1" to="#listlen(cols,',')#" index="L">
<cfset colstyp = ListAppend(colstyp,"VarChar",",")>
</cfloop>
<!--- Create a new query. --->
<cfset variables.qryclsvar = queryNew(
'#cols#',
'#colstyp#'
)>
<cfset variables.qryclsvarfg = false>
</cfif>
<cfset QueryAddRow(variables.qryclsvar, 1)>
<cfloop collection="#vstructobj#" item="sd">
<cfset QuerySetCell(variables.qryclsvar, "#sd#", vstructobj[sd])>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="structit">
</cfcatch>
</cftry>
<cfreturn variables.qryclsvar>
</cffunction>
</cfcomponent>
CFM : 3
<cfset sgObj = createobject("component","cfc.mycfc") />
<cfset mystruct = sgObj.APItoquery(APIobj= arr1) >
<cfdump var="#mystruct#" label="mystruct">
MA ! ....
I have a ColdFusion 9 application using an application.cfc and it creates the session like it should but when I dump the session there is a cfid and a cftoken and sometimes the urltoken but no sessionid and I can not reference the SESSION.SessionID variable in my code as it says it is undefined.
What would cause it to creat the cfid and the cftoken just fine but no sessionid?
<cfset THIS.Name = "AppNameRedacted" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan( 2, 0, 0, 0 ) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SessionTimeout = CreateTimeSpan( 0, 2, 30, 0 ) />
<cfset THIS.SetClientCookies = true />
EDIT: onSessionStart() function
<cffunction name="OnSessionStart" access="public" returntype="void" output="false" hint="Fires when the session is first created.">
<cfset var LOCAL = {} />
<cfset LOCAL.CFID = SESSION.CFID />
<cfset LOCAL.CFTOKEN = SESSION.CFTOKEN />
<!--- Clear the session. --->
<cfset StructClear( SESSION ) />
<cfset SESSION.CFID = LOCAL.CFID />
<cfset SESSION.CFTOKEN = LOCAL.CFTOKEN />
<!--- Return out. --->
<cfreturn />
</cffunction>
Here are the ColdFusion 9 docs that outline what variables you can expect in the session scope and when.
In your posted code, you are doing a structClear() on the session scope which is deleting all variables from it. The only two variables you're putting back are CFID and CFTOKEN.