I'm trying to set up some REST Web Services with ColdFusion 10, and if I have an onError handler in place in the Application.cfc the error status code is not returned back to to the Consumer.
Let's consider this Application.cfc
<cfcomponent displayname="ApplicationCFC" output="true" >
<cfscript>
this.name = "learnWith";
this.applicationTimeout = createTimeSpan(1,1,0,0);
this.sessionManagement = "false";
this.restsettings.autoregister = true;
this.restsettings.skipCFCWithError = true;
</cfscript>
<cffunction name="onApplicationStart" returntype="boolean" output="true">
<cfset RestInitApplication(expandPath('/myDir'),"lw")>
<cfreturn true>
</cffunction>
</cfcomponent>
Now consider the service, like this:
<cfcomponent rest="true" restpath="/user">
<cffunction name="authenticate" access="remote" restpath="/login" returntype="String" httpmethod="POST"
consumes="application/json" produces="application/json">
<cfset requestData = getHTTPRequestData()>
<cfset userInfo = deserializeJSON(ToString(requestData.content)) />
<!--- cfquery to load user data ---->
<cfif local.dataQuery.recordcount EQ 1>
<cfset local.userVO = createObject()> <!--- create and populate userVo here --->
<cfreturn SerializeJSON(local.userVO)>
<cfelse>
<!--- user not found, throw 401 error --->
<cfthrow type="RestError" errorcode="401" />
</cfif>
</cffunction>
</cfcomponent>
This code works perfectly from Postman. If I pass in the proper credentials
{
"userName": "user",
"password": "hashedPassword
}
I get a user object back as expected with a 200 response.
If I pass in fake or invalid credentials:
{
"userName": "fakeuser",
"password": "fakePassword
}
I get a 401 error back:
That's perfect. However, I want to use a global error handler for the CFC, both for non-REST Service based code and to log possible REST Service errors. If I add this into the Application.cfc
<cffunction name="onError" returnType="void" output="true">
<cfargument name="exception" required="true">
<cfargument name="eventname" type="string" required="true">
<!--- error logging here --->
<cfthrow type="RestError" errorcode="401" />
<!--- or
<cfthrow type="#arguments.exception.Cause.Cause.type#"
errorcode="#arguments.exception.Cause.Cause.code#">--->
</cffunction>
ColdFusion returns a 500 Internal Server error message.
I experimented with cfheader both in the CFC method and within the onError handler:
<cfheader statusCode = "401" statusText = "RestError">
It'll return a 200 with no body instead of a 401 status:
I've tried various iterations of this, but am at a loss. Can I use status codes from a ColdFusion rest service while also having an onError handler in place? And if so, how?
I was able to do what I want. Instead of using cfthrow to create an error I can create the response I needed with restSetResponse.
Inside the cffunction, first make sure to set the return type to void.
Then instead of returning an object or using cfthrow, you want to create a response object manually and send it back using restSetResponse
.
This solved my issue:
<cfcomponent rest="true" restpath="/user">
<cffunction name="authenticate" access="remote" restpath="/login" returntype="void" httpmethod="POST"
consumes="application/json" produces="application/json">
<cfset requestData = getHTTPRequestData()>
<cfset userInfo = deserializeJSON(ToString(requestData.content)) />
<!--- cfquery to load user data ---->
<cfset local.result = structNew()/>
<cfif local.dataQuery.recordcount EQ 1>
<cfset local.userVO = createObject()> <!--- create and populate userVo here --->
<cfset local.result.status = 200 />
<cfset local.result.content = SerializeJSON(local.userVO) />
<cfelse>
<cfset local.result.status = 401 />
<cfset local.result.content = SerializeJSON({message: 'User Not Authorized'}) />
</cfif>
<cfset restSetResponse(local.result) />
</cffunction>
</cfcomponent>
Once I did that I was able to control the response back to my consumer without triggering the onError
method in the Application.cfc
.