I make a REST service call using cfhttp with the throwonerror attribute set to true.
When I use a try/catch statement to capture the error, I can't seem to find a way to output what error the REST service call tried to return, which would reside in the cfhttp.filecontent variable.
I also tried putting the result in a variable. Coldfusion keeps telling me that cfhttp and that variable dont exist. It seems like the call and its result get thrown out the door when an exception is raised.
I've read some advice on setting throwonerror=false and capturing the statuscodes myself, but that seems like silly work. Anyody have any thoughts on this?
<cftry>
<cfhttp url="#restUrl##arguments.method#/#arguments.params#" charset="utf-8" throwonerror="true" result="haha" />
<cfcatch type="any">
<cfdump var="#haha#" />
<cfdump var="#cfcatch#" abort />
</cfcatch>
</cftry>
UPDATE:
I've submited a bug report to Adobe, as Adamn suggested: https://bugbase.adobe.com/index.cfm?event=newBug. I can confirm it works in Railo, but doesn't work on CF10. Adam could also confirm it doesn't work on CF11.
Related
I'm using <cfhttp> to pull in content from another site (coldfusion) and resolveurl="true" so all the links work. The problem I'm having is resolveurl is making the anchor links (href="#search") absolute links as well breaking them. My question is is there a way to make resolveurl="true" bypass anchor links somehow?
For starters, let's use the tutorial code from Adobe.com posted in the comments. You'll want to do something similar.
<cfhttp url="https://www.adobe.com"
method="get" result="httpResp" timeout="120">
<cfhttpparam type="header" name="Content-Type" value="application/json" />
</cfhttp>
<cfscript>
// Find all the URLs in a web page retrieved via cfhttp
// The search is case sensitive
result = REMatch("https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?", httpResp.Filecontent);
</cfscript>
<!-- Now, Loop through those URLs--->
<cfoutput>
<cfloop array="#result#" item="item" index="index">
<cfif LEFT(item, 1) is "##">
<!---Your logic if it's just an anchor--->
<cfelse>
<!---Your logic if it's a full link--->
</cfif>
<br/>
</cfloop>
</cfoutput>
If it tries to return a full URL before the anchor as you say, (I've been getting inconsistent results with resolveurl="true") hit it with this to only grab the bit you want.
<cfoutput>
<cfloop array="#result#" item="item" index="index">
#ListLast(item, "##")#
</cfloop>
</cfoutput>
What this code does is grab all the URLs, and parse them for anchors.
You'll have to decide what to do next inside your loop. Maybe preserve the values and add them to a new array, so you can save it somewhere with the links fixed?
It's impossible to assume in a situation like this.
There does not appear to be a way to prevent CF from resolving the hashes. In our usage of it the current result is actually beneficial since when we present content from another site we usually want the user to be sent there.
Here is a way to replace link href values with just anchor if one is present using regular expressions. I'm sure there are combinations of issues that could occur here if really malformed html.
<cfsavecontent variable="testcontent">
<strong>test</strong>
go to google
go to section
</cfsavecontent>
<cfset domain = replace("current.domain", ".", "\.", "all") />
<cfset match = "(href\s*=\s*(""|'))\s*(http://#domain#[^##'""]+)(##[^##'""]+)\s*(""|')" />
<cfset result = reReplaceNoCase(testcontent, match, "\1\4\6", "all") />
<cfoutput><pre>#encodeForHTML(result)#</pre></cfoutput>
Output
<strong>test</strong>
go to google
<a href="#section>go to section</a>
Another option if you are displaying the content in a normal page with js/jquery available is to run through each link on display and update it to just be the anchor. This will be less likely error with malformed html. Let me know if you have any interest in that approach.
Can anyone tell me, how I can enable exception handling in the theme level EventHandler.cfc in MURA CMS 6?
I have already had to remove the error handling in the Application.cfc error method, because the default routine wasn't displaying enough error detail. But, it seems like the whole CFC framework is wrapped in a <CFTRY> block, which quite frankly is bizarre.
I would prefer a solution that does not involve using <CFCATCH> to dump the errors to a file, which is the temporary solution that I am using at the moment.
I just want Adobe ColdFusion to behave the way it does with my non MURA websites. When there is an error in a CFC, it just displays it, plain & simple.
Of course, for production, my approach is different.
UPDATE:
Just to let you know, I am using Adobe ColdFusion 11, with robust error handling turned on, so I know for fact that this issue is nothing to do with Adobe ColdFusion. It is definitely a MURA CMS issue.
Don't remove the built-in error handling. They have put that in place to protect you from information disclosure. Instead make changes to the error handling to suit your needs.
Mura comes with basically three levels of error "catching". At the theme level, at the site level and then globally. (And I have found that even though an error may be caught at a lower level like 'site' that does not prevent the same error from bubbling up and also firing the 'global' handler.)
Steve Withington created a Gist example that should help you get started. See it here. Be sure to read the comments in the code as it explains where the files live and any configuration settings necessary to invoke them.
Copying his code examples here in case that resource is taken down in the future.Credit Steve Withington
Mura Error Handling: You could use any or even both of the attached methods to help with error handling in Mura CMS. This works better than the default "We're sorry, an error has occurred. Please try again later."
muraCustomErrorFile.cfm
<!---
1) Drop this file under /config/ directory.
2) Add errortemplate=/muraWRM/config/customErrorFile.cfm to the settings.ini.cfm file.
3) Set debuggingenabled=false in the settings.ini.cfm file.
4) Reload Mura CMS
--->
<cftry>
<cfset msg = 'MURA ERROR - MESSAGE: #arguments.exception.Message# DETAIL: #arguments.exception.Detail# ' />
<cflog type="ERROR" file="MuraError" text="#msg#" />
<cfcatch></cfcatch>
</cftry>
<cfparam name="url.debug" default="false" />
<cfoutput>
<!DOCTYPE html>
<html>
<head>
<title>Site Down For Maintenance</title>
</head>
<body>
<h3>Site Down for Maintenance</h3>
<cfif url.debug>
<cfdump var="#arguments#" />
<!--- You Have Access to arguments.eventName and aguments.exception --->
<!---
<h4>Exception Message</h4>
<p>#arguments.exception.message#</p>
<h4>Exception Detail</h4>
<p>#arguments.exception.detail#</p>
<h4>TagContext[1]</h4>
<cfdump var="#arguments.exception.TagContext[1]#" />
--->
<!--- you could also dump whatever else you want to inspect --->
<!---
<cfdump var="#cgi#" label="CGI" />
<cfdump var="#request#" label="REQUEST" />
<cfdump var="#session#" label="SESSION" />
<cfdump var="#application#" label="APPLICATION" />
--->
<cfelse>
<p>This site is temporarily down for maintenance.</p>
</cfif>
</body>
</html>
</cfoutput>
muraOnGlobalError.cfm
<cfscript>
// drop this method in either the Site or Theme eventHandler.cfc
public any function onGlobalError($) {
var tagContext = '';
var local = {};
param name='url.debug' default=false;
local.ex = arguments.$.event('exception');
local.errorMessage = 'GLOBAL ERROR - MESSAGE: #local.ex.Message# DETAIL: #local.ex.Detail# ';
try {
tagContext = local.ex.TagContext[1];
} catch(any e) {};
if ( IsStruct(tagContext) ) {
local.errorMessage = local.errorMessage & '
LINE: #tagContext.LINE#
TEMPLATE: #tagContext.TEMPLATE#
RAW_TRACE: #tagContext.RAW_TRACE#';
}
WriteLog(type='ERROR', file='muraGlobalError', text='#local.errorMessage#');
if ( url.debug ) {
WriteOutput('<h2>Debug Output</h2>' & local.errorMessage);
WriteDump(var=arguments, label='ARGUMENTS', abort=1);
}
}
</cfscript>
Does anyone know if the new websockets feature in CF10 can be used cross domain and cross server? And does anyone know or have some sample code to do this?
I have a simple live help chat working on my app but I want to apply it to other sites and have one central admin chat area where the support agents will interact with users cross domain.
As far as I know they do not. You can, however, use a <cfhttp> to call a file on the other site that will publish the message. Here is I accomplished this.
Create a file called socketPublisher.cfm and save it in a directory that does not require a login access a file.
socketPublisher.cfm
<cfparam name="Request.Attributes.msgType" default="newJob">
<cfparam name="Request.Attributes.channel" default="notify">
<cfparam name="Request.Attributes.Type" default="">
<cfoutput>
<cfswitch expression="#Request.Attributes.Type#">
<cfcase value="yourType">
<cfscript>
WSPublish('chat',{message: '', msgType: '#Request.Attributes.msgType#'});
</cfscript>
</cfcase>
<cfdefaultcase>
<cfscript>
WSPublish('#Request.Attributes.channel#',{message: '', msgType: '#Request.Attributes.msgType#'});
</cfscript>
</cfdefaultcase>
</cfswitch>
</cfoutput>
Then in you action page on the other site, you will need to make your http request to that file.
actionPage.cfm
<cfhttp method="Post" url="#socketURL#/_scripts/socketPublisher.cfm">
<cfhttpparam type="URL" name="msgType" value="pendingFiles">
</cfhttp>
That should do it.
There is also a know issue with CF10 WSPublish that it will change the CGI scope cause error when trying to do a redirect from an action page. I am using this as a workaround for that issue until I can find a better solution.
I am using ColdFusion 9.1.0.
I am working on part of a site that someone else developed a while back. That someone is gone and there is no documentation. I can access the CF Administrator, but I can't find anything that helps me with a specific problem.
In the code below, a Java object is created (auth) and then in the next line, the object is referenced. Two variables are passed to the method (runTransaction), the properties file (VARIABLES.PropsFile) and the XML (VARIABLES.MyXML).
<cfobject action="create" type="Java" class="CyberSource" name="auth">
<cfset VARIABLES.ResponseString = auth.runTransaction(VARIABLES.PropsFile,VARIABLES.MyXML)>
The object is successfully created. I know this because when I change the class of the object, it blows up! When I change it back to "CyberSource", it works.
I know that the properties file exists. I know that the XML exists.
The error I get is this:
The system has attempted to use an undefined value, which usually indicates a programming error, either in your code or some system code.
Null Pointers are another name for undefined values.
The error occurred in D:/inetpub/wwwroot/Watkins_OE-DEV/Test.cfm: line 63
61 : <!--- CREATE JAVA OBJECT --->
62 : <cfobject action="create" type="Java" class="CyberSource" name="auth">
63 : <cfset VARIABLES.ResponseString = auth.runTransaction(VARIABLES.PropsFile,VARIABLES.MyXML)>
Can you provide me with any clues as to what this error really means and where I might look next?
EDIT:
I can't pinpoint what my problem WAS, but I went about solving it a different way. I found a file that WAS working and slowly rebuilt it, testing it 100 times along the way.
Many thanks for the tips and hints!
As per the documentation, wrap your auth.runTransaction() with a try/catch.
The following snippet comes from the samples included with the CyberSource java api:
<!--- Change this to point to your property file --->
<cfset propsFile = "full_path_to/coldfusion/samples/cybs.properties">
<!--- Change this to point to your request file --->
<cffile action="Read" file="full_path_to/coldfusion/samples/xml/request.xml" variable="requestString">
<cfobject action="create" type="Java" class="CyberSource" name="auth">
<cfxml variable="requestDoc">
<cfoutput>#requestString#</cfoutput>
</cfxml>
<cfdump label="Request" var="#requestDoc#">
<cftry>
<!--- make the call --->
<cfset responseString = auth.runTransaction(propsFile,requestString)>
<!--- cast the response to xml --->
<cfxml variable="responseDoc">
<cfoutput>#responseString#</cfoutput>
</cfxml>
<cfdump label="Response" var="#responseDoc#">
<!--- exception handling --->
<cfcatch type="com.cybersource.ws.client.ClientException">
<cfoutput><b>Exception Message:</b> #cfcatch.message#</cfoutput><br><br>
<cfdump label="com.cybersource.ws.client.ClientException" var=#cfcatch#>
</cfcatch>
<cfcatch type="com.cybersource.ws.client.FaultException">
<cfoutput><b>Exception Message:</b> #cfcatch.message#</cfoutput><br><br>
<cfdump label="com.cybersource.ws.client.FaultException" var=#cfcatch#>
</cfcatch>
</cftry>
There are some coldfusion samples inside the All Platforms Java zip downloadable here:
http://apps.cybersource.com/cgi-bin/pages/dev_kits.cgi?kit=Java/All_Platforms
Let's say i've just parsed someone else's XML document which is a response to an API request. I want to know if a value nested deep inside exists. If my API request worked, it will be in the same place every time. If my API request fails, the root of the XML is very different.
If I try <cfif structKeyExists(myStruct.level1.level2.level3, 'myTarget')> on a failed api request, I get the fatal error: Element LEVEL1.LEVEL2 is undefined in MYSTRUCT.
Of course, I could try to depend on the root level of the XML telling me of success or failure, and not looking for the result if it failed, but... barring that solution, what should i do?
Do i need to check for the existence of each level of the struct? As in:
<cfif structKeyExists(myStruct, 'level1')
and structKeyExists(myStruct.level1, 'level2')
and structKeyExists(myStruct.level1.level2, 'level3')
and structKeyExists(myStruct.level1.level2.level3, 'myTarget')>
<!--- ... --->
</cfif>
This is not a real-world problem, this is just something i've faced too many times. Please don't tell me solutions that involve changing the API or solutions like those in the third paragraph.
Thanks!
edit: i should have mentioned why i can't use isDefined() - some of the keys do not have syntactically valid names, so isDefined() throws an error, eg myStruct.level1[42].level3
XMLSearch
I would use the parsed XML document (i.e. xmlDoc) and XMLSearch:
<cfset xmlDoc = xmlParse(responseData)>
<cfset nodes = XmlSearch(xmlDoc, '/level1/level2/level3/myTarget')>
<cfif arrayLen(nodes)>
<!--- do something, you have the "nodes" array to work with too --->
</cfif>
xpath for XMLSearch() assumes the structure keys are nodes. You would need to modify accordingly if, for instance, 'myTarget' is an attribute of a node.
StructFindKey
Another way of doing this would be StructFindKey.
<cfset result = structFindKey(myStruct, "myTarget")>
<cfif arrayLen(result) AND result.path EQ "level1.level2.level3">
<!--- do something --->
</cfif>
Conclusion
Haven't tested, but I believe either will be faster than using IsDefined() or a try-catch block. Has the advantage over XMLValidate() of not needing a DTD. And, even with a DTD, the node you want may be defined as optional, so it could still validate.
You could validate the XML against a DTD to make sure the document was in the right format. XmlParse() and XmlValidate() both take a DTD as a parameter.
<cfset validateResult = XmlValidate(myXmlDocument, myDTD)>
<cfif validateResult.status>
<!--- xml is valid continue processing --->
<cfelse>
<!--- xml did not validate handle the error --->
</cfif>
Personally I wouldn't go crazy checking for every level of a 'deep' structure like this. I would presume that if the top level exists the rest of the document will be as you expect, and I'd just address the document from there.
If you wanted you could perhaps try to address the value in your struct and wrap it in a try/catch. That way you can handle any errors at any 'level' in the same way.
<cftry>
<cfset myVar = myStruct.level1.level2.level3 />
<cfcatch type="any">
<!--- Handle error --->
</cfcatch>
</cftry>
Hope that helps some.
I know I'm going to get booed off the stage here, but this is where isDefined() can save you a lot of typing:
<cfif isDefined(structKeyExists(myStruct.level1.level2.level3)>
<!--- do something --->
</cfif>
I know this is a year old, but I'm going to put in an answer here. I struggled for a good long time with this one, till I found a simple solution. If I know the structure of the XML already, a simple IsDefined works to test if the node or node attribute exists. I don't think most people know you can do this, or have tried and failed because they didn't include single quotes in the IsDefined function.
So say I grab some user xml from a web service somewhere and want to display the user's ID.
<cfhttp url="https://mycompany.com/mywebservices/getusers" username="me" password="mysecret">
<cfset userXMLDoc = XMLParse(ToString(cfhttp.FileContent).trim())>
<cfif IsDefined('userXMLDoc.Data.Record.User.XmlAttributes.id')>
<cfdump var="#userXMLDoc.Data.Record.User.XmlAttributes.id#">
<cfelse>
<cfoutput>Failed: No User ID found</cfoutput>
</cfif>