I've always used a transferBean to return data from the service layer. Just
a bean with some minimal logic which will allow wrapping any kind of value.
Sounds very much like what you're talking about, only as an object and not a
raw struct, and since it is an object, it would allow you to keep the code
to return specific status codes in one place and have enhanced
response-shaping functionality in one place.
The only potential problem that I could think of is not having 'enough'
status codes to represent all error conditions you'd need to internally,
which would force you to create proprietary ones like a 400.12 or a 500.4,
whatever the hell those are, but I suppose you could still ultimately map
those back to a 'legitimate' set of response codes inside the transferBean.
Here's a typical call which returns a transferBean:
<cfset temp = variables.projectService.someMethod()><!--- returns a
transferBean --->
<cfif temp.hasError()>
<cfthrow type="#temp.getError().type#"
message="#temp.getError().message#"><!--- the error value is typically a
cfcatch var from the service method --->
<cfelse>
<cfset responseThing = temp.getValue()>
</cfif>
And below is the transfer object I've been using for years. I could see you
adding a REST-specific version of the getValue() function, say a
getResponse() method, which returns the struct you're talking about.
Basically, you would return the transferBean with whatever native value
you'd normally return and just add some transformation methods in the bean;
I don't know that I'd feel comfortable modifying my service layer components
so much since all the apis I've done have been integrated within larger
applications and the api and the main app share many of the same components.
<cfcomponent displayname="Transfer Object" hint="A Bean for encapsulating
data transfers between layers">
<cfset variables.instance = structnew()>
<cfset variables.instance.value = "">
<cfset variables.instance.message = "">
<cfset variables.instance.error = structnew()>
<cffunction name="init" access="public" hint="Initializes
the object" output="no" returntype="transferBean">
<cfargument name="value" required="false"
type="any">
<cfargument name="message" required="false"
type="string">
<cfargument name="error" required="false"
type="struct">
<cfif structkeyexists(arguments,"value")>
<cfset setValue(arguments.value)>
</cfif>
<cfif structkeyexists(arguments,"message")>
<cfset setMessage(arguments.message)>
</cfif>
<cfif structkeyexists(arguments,"error")>
<cfset setError(arguments.error)>
</cfif>
<cfreturn this>
</cffunction>
<cffunction name="setValue" access="private" hint="sets the
value property" output="no" returntype="void">
<cfargument name="value" required="yes" type="any">
<cfset variables.instance.value = arguments.value>
</cffunction>
<cffunction name="getValue" access="public" hint="returns
the value property" output="no" returntype="any">
<cfreturn variables.instance.value>
</cffunction>
<cffunction name="setMessage" access="private" hint="sets
the message property" output="no" returntype="void">
<cfargument name="message" required="yes"
type="string">
<cfset variables.instance.message =
arguments.message>
</cffunction>
<cffunction name="getMessage" access="public" hint="returns
the message property" output="no" returntype="string">
<cfreturn variables.instance.message>
</cffunction>
<cffunction name="setError" access="public" hint="sets the
error property" output="no" returntype="void">
<cfargument name="error" required="yes" type="any">
<cfset variables.instance.error = arguments.error>
<cfif not structisempty(arguments.error)>
<cfset request.magicErrorOverrideValue =
true>
</cfif>
</cffunction>
<cffunction name="getError" access="public" hint="returns
the error property" output="no" returntype="any">
<cfreturn variables.instance.error>
</cffunction>
<cffunction name="hasError" access="public" hint="returns
whether or not the object contains error info" output="no"
returntype="boolean">
<cfif structisempty(variables.instance.error)>
<cfreturn false>
<cfelse>
<cfreturn true>
</cfif>
</cffunction>
<cffunction name="getValueDatatype" access="public"
hint="returns the datatype of the object's value" output="no"
returntype="string">
<cfscript>
if(isArray(variables.instance.value)) {
return 'Array'; }
else if(isBinary(variables.instance.value))
{ return 'Binary'; }
else if(isNumeric(variables.instance.value))
{ return 'Numeric'; }
else if(isDate(variables.instance.value)) {
return 'Date'; }
else if(isQuery(variables.instance.value)) {
return 'Query'; }
else if(isStruct(variables.instance.value))
{ return 'Struct'; }
else if(isWDDX(variables.instance.value)) {
return 'WDDX'; }
else if(isJSON(variables.instance.value)) {
return 'JSON'; }
else if(isBoolean(variables.instance.value))
{ return 'Boolean'; }
else
if(isCustomFunction(variables.instance.value)) { return 'Function'; }
else
if(isSimpleValue(variables.instance.value)) { return 'String'; }
else { return 'Unknown'; }
</cfscript>
</cffunction>
</cfcomponent>
I'm not sure I'm 100% with you on thinking REST 'all the way through'. I
think I see what you're saying, but not having tried it, I guess I'd be
afraid that it would limit my flexibility with the service layer when what I
really want to return/am used to returning is a Boolean or a query or a
struct, or what have you. By using the transferBean, you could continue to
have service methods return whatever they 'should' and shape the
REST-focused response struct in the bean itself.
Kevin
> --
> You received this message because you are subscribed to the Google Groups
> "cfrest" group.
> To post to this group, send email to cfr...@googlegroups.com.
> To unsubscribe from this group, send email to
> cfrest+un...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/cfrest?hl=en.
In my onRequestStart method, I initialize a 'status' struct in the request
scope which my components' methods set and read during processing:
request.dc.status.success = false;
request.dc.status.haserror = false;
request.dc.status.message = "";
request.dc.status.errorfields = ""; // list of form fields which require
error highlighting, if any
Then, for remoting requests, I will always return this json-serialized
struct in the response, using the request.dc.status struct as the source for
the json response, then I don't have to putz around with crafting error and
success messages, they are already there as the result of the processing.
This status struct always exists both on the server side and in the client
side response it certainly does make things easier, knowing that you always
can rely on it to exist and contain information relevant to the process and
its result.
Here's an example of a jquery callback function I use in ajax form
processing:
function(data) {
var success = false;
var haserror = false;
var message = "";
var errorfields = "";
var j;
try {
j = $.parseJSON(data);
success = j.SUCCESS;
haserror = j.HASERROR;
message = j.MESSAGE;
errorfields = j.ERRORFIELDS;
}
catch(e){
haserror = true;
message = "An unexpected error occurred.";
}
renderAjaxMessage(success,haserror,message,errorfields); //
this just throws up a div to display success or error messaging
}
FWIW, I always force uppercase my json serialized from cf to remove any
question as to case. Anyway, if you're saying that the same struct should
exist from service layer (and other) calls as well as at the final response
to the client, I totally agree with that. I do something similar with my
api responses as well so that there is actionable information to go off of,
and it's also based upon that same basic status struct.
I was looking here http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html to
see if I could better understand what you're saying about using the status
codes at the service layer to denote success/error conditions, and while I
certainly see some alignment between HTTP status codes and common responses
from service calls, I think I'm still not seeing the light on how that move
would be a good thing within the app itself, rather than just when preparing
the response to the client. For example, what would you use for 'required
parameter missing', or the like? Is this even what you're talking about,
using the standard spectrum of HTTP status codes internally to denote the
state of processing at the cf level?
Kevin
.
I return transferBeans not 'status' structs from my service layer to my
handlers (controllers); rather, the handlers make calls to the service
components, get the results of those calls, and update the status struct as
required. My service layer doesn't deal directly with responses to anything
other than other components, so it was misleading to imply that they did,
below.
When I was talking about having a struct start at the server and exist
through to the client response, I was referring to it existing at the
controller level, not service.
Kevin
> -----Original Message-----
> From: cfr...@googlegroups.com [mailto:cfr...@googlegroups.com] On