G'day:
The emphasis in the title is an allusion to
web socket requests also not respecting them.
God knows what this article will read like. I headed to the pub to catch the last quarter of the
Aussie v Lions match, and I've been sitting here since. I'm over in Portumna visiting my son, and other than my 4h appointment doing that, I've kinda got nothing better to do here, so I'm still in the pub (which has wireless), amusing myself and drinking Guinness. Am just finishing my third pint. And will order a fourth. It's 3pm ;-)
Anyway, someone posted a question on Stack Overflow about how
erroring REST requests don't fire onError() calls. This is a known issue:
3506757. But it was enough for me to furrow my brow and decide to have a look into it.
Now I'm no REST expert (neither in the context of ColdFusion, nor in general), so it always takes me a bit to get a REST CFC / function working, but I've come up with this test environment:
// Application.cfc
component {
this.name = "testappCfcIssue09";
public void function onApplicationStart(){
restInitApplication(expandPath("../api"), "api");
}
public void function onError(){
writeLog(file=this.name, text="#GetFunctionCalledName()# called");
writeDump(var=local, label=this.name);
abort;
}
}
This is a fairly vanilla Application.cfc. The
onApplicationStart() stuff is just to work around some lazy programming on the part of Adobe, in that when one changes a REST-aware CFC, it will error until one restarts the REST engine. The
onError() is just there in case REST calls call
this onError() instead of the one in the directory the REST CFCs are in (I came into this not knowing how this stuff was handled at all, so just making sure).
<!--- testException.cfm --->
<cfhttp method="get" url="http://#CGI.http_host#/rest/api/Messaging/forceError" result="response">
</cfhttp>
<cfdump var="#response#" label="HTTP Response">
[I have decided against Guinness for the fourth: it's cider. It's a cracking hot day here today in Portumna, so not really Guinness weather]
This calls my REST method, and dumps the response. Now, over in the API dir, I have this lot:
//Messaging.cfc
component rest=true {
remote string function gday(required string name restargsource="query") httpmethod="get" {
return "G'day #name#";
}
remote string function forceError() httpmethod="get" restpath="forceError" {
throw(type="ForcedException", message="Exception forced", detail="Because you asked me to");
}
}
The first method is just me testing that stuff was actually working properly with a vanilla method. The second method is one that forces an error, specifically so I can see what happens when an exception occurs.
And I have this Application.cfc in this directory as well, to log
everything which goes on during REST requests:
// Application.cfc
component {
this.name = "restRequestLogging02";
this.sessionManagement = true;
public void function onApplicationStart(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
}
public void function onApplicationEnd(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
}
public void function onSessionStart(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
}
public void function onSessionEnd(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
}
public void function onRequestStart(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
}
public void function onRequestEnd(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
}
public void function onRequest(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
include arguments[1];
}
public any function onCfcRequest(required string cfc, required string method, required struct args){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
var o = createObject(arguments.cfc);
var metadata = getMetadata(o[method]);
if (structKeyExists(metadata, "access") && metadata.access == "remote"){
return invoke(o, method, args);
}else{
throw(type="InvalidMethodException", message="Invalid method called", detail="The method #method# does not exists or is inaccessible remotely");
}
}
public void function onError(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
writeDump(local);
abort;
}
public void function onMissingTemplate(){
writeLog(file=this.name, text="#getFunctionCalledName()# called");
writeeDump(local);
abort;
}
}
The only non-obvious method in this is the
onCfcRequest() one. The code here is reflective of the
unexpected "security issue" onCfcRequest() could effect in an application that I blogged about a while ago. Any
onCfcRequest() method should do this lot
as a minimum. That consideration aside, basically every event handler call is logged. That's the crux of things.