Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 10:30
Show Gist options
  • Save bennadel/9758853 to your computer and use it in GitHub Desktop.
Save bennadel/9758853 to your computer and use it in GitHub Desktop.
Using OnMissingMethod() With Remote Access ColdFusion Components
<cfcomponent
output="false"
hint="I provide the core remote proxy API functionality.">
<cffunction
name="NewAPIResponse"
access="public"
returntype="struct"
output="false"
hint="I provide a new default API response object.">
<!--- Define the local scope. --->
<cfset var LOCAL = {} />
<!--- Create a new API response object. --->
<cfset LOCAL.Response = {
Success = true,
Errors = [],
Data = ""
} />
<!--- Return new response object. --->
<cfreturn LOCAL.Response />
</cffunction>
<cffunction
name="ManuallyReturnReponse"
access="public"
returntype="void"
output="false"
hint="I stream the given response to the client manually using CFHeader and CFContent.">
<!--- Define arguments. --->
<cfargument
name="Response"
type="struct"
required="true"
hint="I am the API response being returned."
/>
<cfargument
name="ReturnFormat"
type="string"
required="false"
default="JSON"
hint="I am the format required for the API response."
/>
<!--- Define the local scope. --->
<cfset var LOCAL = {} />
<!---
Create the response string. Check to see if we want
this as WDDX or JSON.
--->
<cfswitch expression="#ARGUMENTS.ReturnFormat#">
<cfcase value="wddx">
<!--- Convert to WDDX. --->
<cfwddx
action="cfml2wddx"
input="#ARGUMENTS.Response#"
output="LOCAL.ResponseString"
/>
</cfcase>
<cfdefaultcase>
<!---
By default, we are going to return the API
response in JSON string (since that is what
our appliation does by default).
--->
<cfset LOCAL.ResponseString = SerializeJSON(
ARGUMENTS.Response
) />
</cfdefaultcase>
</cfswitch>
<!---
Now that we have our API response string, we need
to update the headers and return the response.
--->
<!---
Set header response code to be 200 - remember, the
whole point of the unified API response is that it
never "fails" unless there is truly a request
exception.
--->
<cfheader
statuscode="200"
statustext="OK"
/>
<!--- Steam binary contact back. --->
<cfcontent
type="text/plain"
variable="#ToBinary( ToBase64( LOCAL.ResponseString ) )#"
/>
<!---
At this point, the request has been completely
committed and cannot be altered.
--->
<!--- Return out. --->
<cfreturn />
</cffunction>
<cffunction
name="OnMissingMethod"
access="public"
returntype="struct"
output="false"
hint="I handle non-explicit API messaging.">
<!--- Define arguments. --->
<cfargument
name="MethodName"
type="string"
required="true"
hint="I am the name of the method."
/>
<cfargument
name="MethodArguments"
type="struct"
required="true"
hint="I am the collection of arguments."
/>
<!---
Use the error response to create and return an API
response with the given API error.
--->
<cfreturn THIS.NewErrorResponse( "The method that you requested, #ARGUMENTS.MethodName#, is not supported in the current version of this API." ) />
</cffunction>
<cffunction
name="NewErrorResponse"
access="public"
returntype="struct"
output="false"
hint="I create an return a new error response with the given error.">
<!--- Define arguments. --->
<cfargument
name="Error"
type="string"
required="true"
hint="I am the error message."
/>
<!--- Define the local scope. --->
<cfset var LOCAL = {} />
<!--- Create a new API response object. --->
<cfset LOCAL.Response = THIS.NewAPIResponse() />
<!--- Flag it as not successful. --->
<cfset LOCAL.Response.Success = false />
<!--- Set the error message. --->
<cfset LOCAL.Response.Errors[ 1 ] = {
Property = "",
Error = ARGUMENTS.Error
} />
<!--- Return new response. --->
<cfreturn LOCAL.Response />
</cffunction>
</cfcomponent>
<cfcomponent
output="false"
hint="I provide application settings and event handlers.">
<cffunction
name="OnError"
access="public"
returntype="void"
output="true"
hint="I handle uncaught application errors.">
<!--- Define the arguments. --->
<cfargument
name="Exception"
type="any"
required="true"
hint="I am the uncaught exception."
/>
<!--- Define the local scope. --->
<cfset var LOCAL = {} />
<!---
Normally, we would want to return a error header, but
if the request was an API call (remote call), we never
want to return an error. Rather, we want to return an
"unsuccessful" API request.
--->
<cfif REFindNoCase( "\.cfc$", CGI.script_name )>
<!---
Create an instance of the CFC in question.
NOTE: This line of code leverages the undocumented
ability for ColdFusion components to be created
both dot-delimited AND slash-delimited file paths.
--->
<cfset LOCAL.API = CreateObject(
"component",
REReplaceNoCase(
CGI.script_name,
"\.cfc$",
"",
"one"
)
) />
<!--- Set default return format. --->
<cfset LOCAL.ReturnFormat = "JSON" />
<!--- Check to see if return format was overriden. --->
<cfif StructKeyExists( URL, "ReturnFormat" )>
<!--- Overriding default format. --->
<cfset LOCAL.ReturnFormat = URL.ReturnFormat />
</cfif>
<!---
Check to see what kind of error we had. If the
"func" key exists, then it was a missing method
error. If not, then it was simply an unhandled
error.
--->
<cfif StructKeyExists( ARGUMENTS.Exception, "Func" )>
<!--- Missing method error. --->
<!--- Build up arguments. --->
<cfset LOCAL.Arguments = Duplicate( URL ) />
<cfset StructAppend( LOCAL.Arguments, FORM ) />
<!---
Call onMissingMethod and stream back response
to client manually.
--->
<cfset LOCAL.API.ManuallyReturnReponse(
LOCAL.API.OnMissingMethod(
LOCAL.Arguments.Method,
LOCAL.Arguments
),
LOCAL.ReturnFormat
) />
<cfelse>
<!--- Uncaught exception. --->
<!---
Create a new error and return it to the client
using explicit stream.
--->
<cfset LOCAL.API.ManuallyReturnReponse(
LOCAL.API.NewErrorResponse(
ARGUMENTS.Exception.Message
),
LOCAL.ReturnFormat
) />
</cfif>
</cfif>
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment