Coldfusion onCFCRequest将XML的返回类型更改为WDDX [英] Coldfusion onCFCRequest Changing Return Type of XML Into WDDX
问题描述
我的客户群终于离开了Coldfusion 8,因此现在我可以利用Coldfusion 9的 Application.cfc-> onCFCRequest
事件。我有一个测试方案设置,但结果与预期不符。我有一个可以调用的方法,该方法可以生成有效的 XML
响应,像这样...
My customer base is finally off Coldfusion 8 so now I can take advantage of Coldfusion 9's Application.cfc -> onCFCRequest
event. I have a test scenario setup and my result is not what I'm expecting. I have a method that I call which produces a valid XML
response like so...
Response Header: Content-Type:application/xml;charset=UTF-8
Response:
<?xml version="1.0" encoding="UTF-8"?>
<rows><row id="10000282742505"><cell/><cell> ...
现在,在我介绍 onCFCRequest
事件我回来了(这打破了我的网格)...
Now after I introduce the onCFCRequest
event I get this back (which breaks my grids)...
Response Header: Content-Type:application/xml;charset=UTF-8
Response:
<wddxPacket version='1.0'><header/><data><string><rows><row id="10000282742505"><cell></cell><cell> ...
这是事件...
<cffunction name="onCFCRequest" access="public" returntype="Any" output="true">
<cfargument type="string" name="cfc" required="true">
<cfargument type="string" name="method" required="true">
<cfargument type="struct" name="args" required="true">
<cfscript>
// OnCFCRequest security hole fix as detailed here: http://blog.adamcameron.me/2013/04/its-easy-to-create-security-hole-in.html
var o = createObject(ARGUMENTS.cfc);
var metadata = getMetadata(o[ARGUMENTS.method]);
if (structKeyExists(metadata, "access") && metadata.access == "remote"){
return invoke(o, ARGUMENTS.method, ARGUMENTS.args);
}else{
throw(type="InvalidMethodException", message="Invalid method called", detail="The method #method# does not exists or is inaccessible remotely");
}
</cfscript>
<cfreturn />
</cffunction>
如何使onCFCRequest以与远程功能相同的格式传递响应返回?
我可能最终会尝试这样做,但首先我想弄清楚为什么我不能简单地以相同的格式传递响应。
I will probably end up trying that but first I'd like to figure out why I can't simply pass through the response in the same format.
推荐答案
看来我不能简单地传递结果因此具有相同的格式... 默认情况下,ColdFusion将除XML外的所有返回类型(包括简单返回类型)序列化为WDDX格式,并以XML文本返回XML数据。( http://help.adobe.com/zh_CN/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7f5c.html )。调用的远程函数将我的 xml
作为字符串返回给 onCFCRequest
, onCFCRequest 然后code>将该简单返回类型(此时为字符串)转换为
WDDX
,因为这是默认行为。
It appears I can't simply pass through the result with the same format because of this... "By default, ColdFusion serializes all return types (including simple return types), except XML, into WDDX format, and returns XML data as XML text." (http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7f5c.html). The remote function being called returns my xml
as a string to the onCFCRequest
, onCFCRequest
then converts that simple return type (a string at this point) into WDDX
because that's the default behavior.
所以...
经过大量测试,我最终选择了Ben Nadel文章中的解决方案,但是我想提一些调整。
After a lot of testing I ended up going with the solution from Ben Nadel's article, but with a few tweaks I'd like to mention.
- 第一个添加是添加问题中已经显示的代码,用于查找Adam Cameron发现的错误
- 第二个附加功能是在调用之后立即执行此操作:
< cfset local.result = ToString(local.result)>
。 Ben在评论中说: ...所有返回的值都将是字符串... ,但事实并非如此。我们有一些仅返回数字的远程功能。如果没有ToString()
,则将响应转换为二进制的代码将进一步失败。 - 在mimeTypes所在的部分中设置我更改了
json
的IF语句。在我们编写的每个远程函数中,我们创建一个ColdFusion结构,然后将其返回,如下所示:< cfreturn SerializeJSON(z.response)/>
。这似乎比手动将json字符串拼凑起来然后在onCFCRequest
中序列化要容易得多。因此,在json mimeType的onCFCRequest中,我只是将其视为字符串,因为它已经被序列化了; - 还在mimeType部分中,我为
xml
添加了IF语句。我们有很多远程功能,它们向网格吐出xml
而不是向wddx
吐出。而且由于没有xml
的returnFormat
,因此我添加了returnType 在
wddx
支票上方的xml
中的code>。 - 将
JSON
和XML
的responseMimeType更改为application / json
和每个@Henry的评论application / xml
。谢谢!
- The first addition was to add the code already shown in my question for the bug Adam Cameron found.
- The second addition was to do this right after the invoke:
<cfset local.result = ToString(local.result)>
. In Ben's comments it says "...all the values returned will be string..." but that doesn't seem to be the case. We have some remote functions that only return a number. Without theToString()
the code further down that converts the response to binary fails. - Down in the section where the mimeTypes are being set I altered the IF statement for
json
. In every remote function we've written we make a ColdFusion structure then return it like so:<cfreturn SerializeJSON(z.response) />
. This seems a lot easier than piecing together a json string manually, THEN serializing it in theonCFCRequest
. So in the onCFCRequest for the json mimeType I just treat it as a string because it's already serialized; so no need to serialize it second time. - Also in the mimeType section I added an IF statement for
xml
. We have many remote functions which spit outxml
for grids, notwddx
. And since there is not areturnFormat
ofxml
I added a check for thereturnType
ofxml
right above thewddx
check. - Changed the responseMimeType for
JSON
andXML
toapplication/json
andapplication/xml
per @Henry's comment. Thanks!
非常感谢Ben和Adam奠定基础!
Thank you much Ben and Adam for laying the ground work!
这是最终结果...
<cffunction name="onCFCRequest" access="public" returntype="void" output="true" hint="I process the user's CFC request.">
<cfargument name="component" type="string" required="true" hint="I am the component requested by the user." />
<cfargument name="methodName" type="string" required="true" hint="I am the method requested by the user." />
<cfargument name="methodArguments" type="struct" required="true" hint="I am the argument collection sent by the user." />
<!---
Here we can setup any request level variables we want
and they will be accessible to all remote cfc calls.
--->
<cfset request.jspath = 'javascript'>
<cfset request.imgpath = 'images'>
<cfset request.csspath = 'css'>
<!---
Check to see if the target CFC exists in our cache.
If it doesn't then, create it and cached it.
--->
<cfif !structKeyExists( application.apiCache, arguments.component )>
<!---
Create the CFC and cache it via its path in the
application cache. This way, it will exist for
the life of the application.
--->
<cfset application.apiCache[ arguments.component ] = createObject( "component", arguments.component ) />
</cfif>
<!---
ASSERT: At this point, we know that the target
component has been created and cached in the
application.
--->
<!--- Get the target component out of the cache. --->
<cfset local.cfc = application.apiCache[ arguments.component ] />
<!--- Get the cfcs metaData --->
<cfset var metadata = getMetaData( local.cfc[ arguments.methodName ] )>
<!--- OnCFCRequest security hole fix as detailed here: http://cfmlblog.adamcameron.me/2013/04/its-easy-to-create-security-hole-in.html --->
<cfif structKeyExists(metadata, "access") and metadata.access eq "remote">
<!--- Good to go! --->
<cfelse>
<cfthrow type="InvalidMethodException" message="Invalid method called" detail="The method #arguments.methodName# does not exists or is inaccessible remotely">
</cfif>
<!---
Execute the remote method call and store the response
(note that if the response is void, it will destroy
the return variable).
--->
<cfinvoke returnvariable="local.result" component="#local.cfc#" method="#arguments.methodName#" argumentcollection="#arguments.methodArguments#" />
<!---
We have some functions that return only a number (ex: lpitems.cfc->get_lpno_onhandqty).
For those we must convert the number to a string, otherwise, when we try to
convert the response to binary down at the bottom of this function it will bomb.
--->
<cfset local.result = ToString(local.result)>
<!---
Create a default response data variable and mime-type.
While all the values returned will be string, the
string might represent different data structures.
--->
<cfset local.responseData = "" />
<cfset local.responseMimeType = "text/plain" />
<!---
Check to see if the method call above resulted in any
return value. If it didn't, then we can just use the
default response value and mime type.
--->
<cfif structKeyExists( local, "result" )>
<!---
Check to see what kind of return format we need to
use in our transformation. Keep in mind that the
URL-based return format takes precedence. As such,
we're actually going to PARAM the URL-based format
with the default in the function. This will make
our logic much easier to follow.
NOTE: This expects the returnFormat to be defined
on your CFC - a "best practice" with remote
method definitions.
--->
<cfparam name="url.returnFormat" type="string" default="#metadata.returnFormat#" />
<cfparam name="url.returnType" type="string" default="#metadata.returnType#" /> <!--- Added this line so we can check for returnType of xml --->
<!---
Now that we know the URL scope will have the
correct format, we can check that exclusively.
--->
<cfif (url.returnFormat eq "json")>
<!--- Convert the result to json. --->
<!---
We already serializeJSON in the function being called by the user, this would cause double encoding, so just treat as text
<cfset local.responseData = serializeJSON( local.result ) />
--->
<cfset local.responseData = local.result />
<!--- Set the appropriate mime type. --->
<cfset local.responseMimeType = "application/json" />
<!---
There is no returnFormat of xml so we will check returnType instead.
This leaves the door open for us to use wddx in future if we decide to.
--->
<cfelseif (url.returnType eq "xml")>
<!--- Convert the result to string. --->
<cfset local.responseData = local.result />
<!--- Set the appropriate mime type. --->
<cfset local.responseMimeType = "application/xml" />
<cfelseif (url.returnFormat eq "wddx")>
<!--- Convert the result to XML. --->
<cfwddx action="cfml2wddx" input="#local.result#" output="local.responseData" />
<!--- Set the appropriate mime type. --->
<cfset local.responseMimeType = "application/xml" />
<cfelse>
<!--- Convert the result to string. --->
<cfset local.responseData = local.result />
<!--- Set the appropriate mime type. --->
<cfset local.responseMimeType = "text/plain" />
</cfif>
</cfif>
<!---
Now that we have our response data and mime type
variables defined, we can stream the response back
to the client.
--->
<!--- Convert the response to binary. --->
<cfset local.binaryResponse = toBinary( toBase64( local.responseData ) ) />
<!---
Set the content length (to help the client know how
much data is coming back).
--->
<cfheader name="content-length" value="#arrayLen( local.binaryResponse )#" />
<!--- Stream the content. --->
<cfcontent type="#local.responseMimeType#" variable="#local.binaryResponse#" />
<!--- Return out. --->
<cfreturn />
</cffunction>
这篇关于Coldfusion onCFCRequest将XML的返回类型更改为WDDX的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!