2014-04-02 39 views
3

我的客戶羣終於關閉了Coldfusion 8,現在我可以利用Coldfusion 9的Application.cfc -> onCFCRequest事件。我有一個測試場景設置,我的結果不是我期待的。我有我打電話產生像這樣一個有效XML響應的方法...Coldfusion onCFCRequest將XML的返回類型更改爲WDDX

Response Header: Content-Type:application/xml;charset=UTF-8 
Response: 
<?xml version="1.0" encoding="UTF-8"?> 
<rows><row id="10000282742505"><cell/><cell> ... 

現在經過我介紹onCFCRequest事件中,我得到這個回(這打破了我的網格)...

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

我最終可能會嘗試這一點,但首先我想弄清楚爲什麼我不能簡單地通過相同的格式的響應。

回答

1

看來我不能簡單地用相同的格式通過的結果,因爲這個......「默認情況下,ColdFusion的串行化所有返回類型(包括簡單的返回類型),除了XML,轉換爲WDDX格式,並將XML數據作爲XML文本返回。「」(http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7f5c.html)。被調用的遠程函數返回我的xml作爲字符串到onCFCRequest,onCFCRequest,然後將簡單返回類型(此時爲字符串)轉換爲WDDX,因爲這是默認行爲。

所以......

大量的測試我結束了從本·納德爾的文章的解決方案去後,但有一些調整,我想提一提。

  1. 第一個補充是添加我的問題中已經顯示的代碼Adam Cameron發現的錯誤。
  2. 第二個補充是在調用後立即執行此操作:<cfset local.result = ToString(local.result)>。在本的評論中,它說「...所有返回的值將是字符串...」,但似乎並非如此。我們有一些只返回一個數字的遠程函數。如果沒有ToString(),將響應轉換爲二進制文件的代碼進一步失敗。
  3. 在mimeTypes設置的部分,我改變了IF語句json。在我們編寫的每一個遠程函數中,我們創建一個ColdFusion結構,然後像這樣返回它:<cfreturn SerializeJSON(z.response) />。這似乎比手工拼接一個json字符串容易得多,然後序列化它在onCFCRequest。因此,在json mimeType的onCFCRequest中,我將它視爲一個字符串,因爲它已經被序列化;所以不需要第二次序列化。
  4. 同樣在mimeType部分,我爲xml添加了IF語句。我們有許多遠程功能,它們爲網格分配xml,而不是wddx。而且由於沒有returnFormat我在wddx支票上面加了returnTypexml
  5. 每@ Henry的評論將JSONXML的responseMimeType更改爲application/jsonapplication/xml。謝謝!

非常感謝Ben和Adam爲奠定基礎工作!

這裏是最後的結果......

<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> 
+1

'application/xml'是首選:http://stackoverflow.com/questions/4832357/whats-the-difference-between-text-xml-vs-application-xml-for-webservice-respons; '應用程序/ json'是首選:http://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type – Henry

+0

謝謝!我更改了它們,並在IE7及以上版本,Chrome,Firefox和Safari中進行了測試。一切都很好。你知道,直到我經歷這個練習時,我總是將'JSON'作爲'text/plain'返回,然後必須將它變成我成功函數中的一個對象:'resultObj = $ .parseJSON(data);'What這是一種快樂,它發現''parseJSON()'行不再需要。設置MIME類型可以正確處理這個問題。簡直不敢相信我從未偶然發現過。 – gfrobenius

1

我從來沒有使用onCfcRequest但你說得對,它有點笨。

似乎onCfcRequest似乎也會「吞下」returnFormat,因此您必須實施自己的returnFormat檢測並將其序列化爲正確的格式。

的OnCFCRequest()方法的返回類型應該是VOID就像 其OnRequest()反一部分。從這個方法返回一個值 似乎沒有發揮任何實際返回頁面 響應中的任何部分。返回一個值,你要麼必須將 方法體中它的輸出,或流回來通過CFContent

引自:http://www.bennadel.com/blog/1647-Learning-ColdFusion-9-Application-cfc-OnCFCRequest-Event-Handler-For-CFC-Requests.htm

例如

... 
var result = invoke(o, ARGUMENTS.method, ARGUMENTS.args); 
... 
<!--- after your </cfscript> ---> 

<!--- TODO: do some checking to determine the preferred return type ---> 

<!--- if detected as xml, serve as xml (safer option) ---> 
<cfcontent type="application/xml" 
      variable="#toBinay(toBase64(local.result))#"> 

<!--- *OR* (cleaner version) watch out for white spaces ---> 
<cfcontent type="application/xml"> 
<cfoutput>#result#</cfoutput> 

+0

感謝@Henry。我想我發現爲什麼我的'returnFormat'正在被改變,或者被吞噬了,就像你說的那樣:我不能簡單地通過結果以相同的格式因爲這個......「默認情況下,ColdFusion序列化所有返回類型(包括簡單的返回類型),除XML之外,轉換爲WDDX格式......「在這種情況下調用的遠程函數將'xml'作爲'string'返回給'onCFCRequest','onCFCRequest'然後將該簡單返回類型(在這一點上是一個字符串)到'WDDX'中,因爲這是默認行爲。我沒事吧,只是很高興現在對我來說更清楚一點:) – gfrobenius

+0

@Gordon人會認爲CF會足夠聰明,可以在整個鏈條中攜帶返回格式,這樣像你這樣的人就不必跳過那麼多箍環。 – Henry