Skip to main content
ccsimmons_FAVER
Inspiring
November 29, 2016
Answered

Sharing variables between cfcs in a RESTful application

  • November 29, 2016
  • 1 reply
  • 1211 views

What is the best method to share variables like the following struct among several cfcs in the same directory of a RESTful API?  There are several cfcs but they all use the same generic error codes.

<cfset RESTErrorCodes={

801={detail="Configuration Error",message="Incorrect API Key",type="RestError"},

802={detail="Database Exception",message="No Records Found",type="RestError"},

803={detail="Usage Error",message="Required argument missing",type="RestError"}

}>

Any help would be greatly appreciated!

This topic has been closed for replies.
Correct answer BKBK

When I use what you suggested in post 7. and call a function to test throwing the custom REST errors I get the error below. 

Element 803 is undefined in a CFML structure referenced as part of an expression.

It seems like the variables.RESTErrorCodes is an empty struct.  When I tried using what I had in post 6 it appears to work.  Am I overlooking something?


You overlooked nothing, I overlooked some. When I looked closer I saw that the problem is a bit more complicated than I at first thought.

If we use init, then it has to be run before any other call to the CFC. Either by the caller or by the CFC itself.

Case 1) Init run by the CFC itself:

If you choose to let the CFC run the init itself, as you have done, then you indeed have to use both lines of code:

<cfset variables.RESTErrorCodes = structNew()>

<cfset init(RESTErrorCodes=application.RESTErrorCodes) />

However, this is not yet optimal, as it still couples the CFC to the application scope.

2) Letting the caller call init:

This is a preferable solution (in an object-oriented sense). But when I look back at your initial question I see a problem I had overlooked. Your CFCs are meant to be RESTful.

This implies calls to them will be stateless. We cannot then expect to carry the result of an init call (the this object) over to the next call. We should therefore think of a solution in which the caller passes an argument, for example, the application-name, to the RESTful CFC. And so we arrive at a possible solution, similar to WolfShade's.

Create a database table, errorCodes, and register it as datasource in the ColdFusion administrator. It will have data as in the following example:

Corresponding to this table will be the CFC errorcodes.cfc, containing the init.

errorcodes.cfc

<cfcomponent>

<cffunction name="init" access="remote" returntype="Struct">

    <cfargument name="appName" type="string" default="myRestApp">

 

    <cfset var RESTErrorCodes = structNew()>

    <!--- For speed and efficiency: My current applicationtimeout value is 2 days, so I cache the query for 2 days --->

    <cfquery name="getErrorCodes" datasource="myDsn" cachedwithin="#createTimespan(2,0,0,0)#">

        select errorkey, errordetail, errormessage, errortype

        from errorCodes

        where applicationName = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.appName#">

        order by errorKey

    </cfquery>

    <!--- Assemble the RESTErrorCodes struct --->

    <cfoutput query="getErrorCodes">   

         <cfset RESTErrorCodes[getErrorCodes.errorkey[currentrow]]={detail=getErrorCodes.errordetail[currentrow],message=getErrorCodes.errormessage[currentrow],type=getErrorCodes.errortype[currentrow]}>

    </cfoutput>

<cfreturn RESTErrorCodes>

</cffunction>

</cfcomponent>

All the caller has to do is pass the application's name along with the other arguments in the REST call. The REST CFCs will be something like

<cfcomponent rest="true" restpath="/etc">

<cffunction name="getSomething" access="remote" httpmethod="get" returntype="any" output="false">

<cfargument name="arg1" type="string" required="yes"/>

<cfargument name="arg2" type="numeric" required="yes"/>

<cfargument name="appName" type="string" required="no" />

<cfset var RESTErrorCodes = structNew()>

<!--- Get the error codes for a particular application --->

<cfset var errorCodeObj = createObject("component", "errorCodes")>

<cfset RESTErrorCodes = errorCodeObj.init(arguments.appName)>

<!--- Rest of business code goes here --->

<cfreturn something>

</cffunction>

</cfcomponent>

1 reply

ccsimmons_FAVER
Inspiring
November 29, 2016

The code was cut from my original post.

<cfset RESTErrorCodes={

801={detail="Configuration Error",message="Incorrect API Key",type="RestError"},

802={detail="Database Exception",message="No Records Found",type="RestError"},

803={detail="Usage Error",message="Required argument missing",type="RestError"}

}>

WolfShade
Legend
November 30, 2016

Not sure if this will work, however..

Create a CFC in the same folder as the CFCs you mentioned, and place one function in it, call it SetCodes.  In that function, set the variable as your code does, then return that variable.

In the top of each CFC that needs those codes, set a variable as such (immediately below the opening CFCOMPONENT tag):

<cfset RESTErrorCodes = new component.componentName().SetCodes() />

Each CFC should now have access to a variable called RESTErrorCodes.  If you add, edit, or delete any of the codes in the SetCodes function, this will be reflected in all instances of the variable RESTErrorCodes at the time of use.

HTH,

^_^