Coldfusion onCFCRequest将XML的返回类型更改为WDDX [英] Coldfusion onCFCRequest Changing Return Type of XML Into WDDX

查看:70
本文介绍了Coldfusion onCFCRequest将XML的返回类型更改为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>&lt;rows&gt;&lt;row id="10000282742505"&gt;&lt;cell&gt;&lt;/cell&gt;&lt;cell&gt; ...

这是事件...

<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以与远程功能相同的格式传递响应返回?

我知道这篇文章: http://www.bennadel.com/blog/1647-Learning-ColdFusion-9-Application-cfc -OnCFCRequest-Event-Handler-For-CFC-Requests.htm

我可能最终会尝试这样做,但首先我想弄清楚为什么我不能简单地以相同的格式传递响应。

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 将该简单返回类型(此时为字符串)转换为 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.


  1. 第一个添加是添加问题中已经显示的代码,用于查找Adam Cameron发现的错误

  2. 第二个附加功能是在调用之后立即执行此操作:< cfset local.result = ToString(local.result)> 。 Ben在评论中说: ...所有返回的值都将是字符串... ,但事实并非如此。我们有一些仅返回数字的远程功能。如果没有 ToString(),则将响应转换为二进制的代码将进一步失败。

  3. 在mimeTypes所在的部分中设置我更改了 json 的I​​F语句。在我们编写的每个远程函数中,我们创建一个ColdFusion结构,然后将其返回,如下所示:< cfreturn SerializeJSON(z.response)/> 。这似乎比手动将json字符串拼凑起来然后在 onCFCRequest 中序列化要容易得多。因此,在json mimeType的onCFCRequest中,我只是将其视为字符串,因为它已经被序列化了;

  4. 还在mimeType部分中,我为 xml 添加了IF语句。我们有很多远程功能,它们向网格吐出 xml 而不是向 wddx 吐出。而且由于没有 xml returnFormat ,因此我添加了 returnType wddx 支票上方的 xml 中的code>。

  5. JSON XML 的responseMimeType更改为 application / json 和每个@Henry的评论 application / xml 。谢谢!

  1. The first addition was to add the code already shown in my question for the bug Adam Cameron found.
  2. 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 the ToString() the code further down that converts the response to binary fails.
  3. 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 the onCFCRequest. 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.
  4. Also in the mimeType section I added an IF statement for xml. We have many remote functions which spit out xml for grids, not wddx. And since there is not a returnFormat of xml I added a check for the returnType of xml right above the wddx check.
  5. Changed the responseMimeType for JSON and XML to application/json and application/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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆