I came across an interesting scenario today using StructCopy with the URL scope.
I'm not looking for answers on how to get around it - I know/have used structAppend/duplicate etc
I wanted to copy the URL scope to a new struct so I can process/alter it without effecting the URL scope itself, so I used structCopy( url ). I know that structcopy is a shallow copy of the struct, but as my url scope will only contain url params (as far as I know - is this correct? they are all I am interested in this case anyway) which are string key/values then I figured a shallow copy would be adequate (string being immutable and all).
However, when copying the URL scope, it resulted in strange behaviour - it appears as though structCopy(url) just returns the url scope - not a copy of it, not an error. For example, if I perform the following (assume I have url query params, including one called "rob":
<cfset local.clonedUrl = structCopy( url ) />
<cfdump var="#local.clonedUrl#">
<cfdump var="#url#">
<cfset structDelete( local.clonedUrl, "rob" ) />
<cfdump var="#local.clonedUrl#">
<cfdump var="#url#">
In the above scenario, after the struct delete call, the clonedUrl and URL have both had the entry removed (obviously, performing the above with a normal created Struct rather than URL in the first place behaves correctly)
So, my questions are:
what? I have seen that the URL scope is actually an instance of coldfusions URLScope class, so maybe treated differently for that reason, but returning the same instance from a structCopy call is just mean. At very least if it can't do it I would expect an error, not a result that looks very similar to expected but actually exactly the behaviour I want to avoid. Also noticed that if I pass a component to structCopy it appears to have similar results.
What is the URLScope - does it extend Struct? When I dump the classname I see it is a URLScope, but just dumping the object dumps it like it would a Struct. Anyone got any advice for viewing either the sourcecode for the object or at least an API/Javadocs type spec?
I'm using CF10 (I'm not looking for answers on how to get around it - I know/have used structAppend/duplicate etc)
Thanks for the comments folks - the conclusion was don't use StructCopy() with the URL object (and possibly on the Form scope object etc). It's pretty grim, but URL isn't technically a struct, so all bets are off. (I moan some more here)
Related
I initially posted this as an answer to this question earlier regarding Empty CGI.REDIRECT_URL on ColdFusion 2016. After thinking about it, I thought better of it since technically didn't answer the OP's question. Instead I decided to make it into a separate question, even though it's more of a commentary than a question. While this technically might not meet the full requirements of a Minimal, Complete, and Verifiable example and people might ding me with downvotes, I decided it was worth it anyway in the hope that it will become easier to find for future CFers who might encounter this. Thus preventing them from banging their head against the wall regarding this peculiar behavior of the CGI struct/scope.
With that said, the CGI struct/scope has some undocumented inconsistent behavior from other structs/scopes. Note that I personally take no credit for this discovery since I happened across this some time ago upon reading Ben Nadel's blog post on this. So all the information I'm posting here is already detailed there, but I wanted to write a nice summary here on SO.
Undocumented behavior 1 - Unlike other structures, if a CGI struct key doesn't exist, then it won't throw an error when referencing it.
In the OP's original question, he was wondering why cgi.REDIRECT_URL existed but was empty. As he eventually found out, it never actually existed. As a separate example you can execute this line of code without throwing an error. Not what you'd expect, huh?
<cfoutout>#cgi.THIS_IS_A_FAKE_KEY#</cfoutout>
So what's a CFer to do? Test for the key existence.
<cfif structKeyExists( CGI, 'THIS_IS_A_FAKE_KEY' )>
THIS_IS_A_FAKE_KEY exists
<cfelse>
THIS_IS_A_FAKE_KEY doesn't exist
</cfif>
Undocumented behavior 2 - Unlike other structures, if you dump the CGI struct, it won't display all the key/value pairs, it will only display a defined set of keys.
In the OP's case, he had a custom Apache CGI variable cgi.REDIRECT_URL that was used in his code prior to upgrading to CF2016 and was able to refer to it directly. However, I'm presuming if he dumped out the cgi struct, it wouldn't appear in the dump. In Ben Nadel's case, he also had a custom cgi variable called cgi.document_root that was passed through from a load balancer and was able to refer to it directly, but he also wasn't able to see the key when dumping the cgi contents.
So what's a CFer to do? Understand this and store it in the back of your mind so you won't get bitten when you dump the cgi contents and the key/value pair isn't there. Other than that, not much else.
I went in to the cfusion.jar file of ColdFusion. What I found there was a bit confusing.
CGI scope is not of a structure form as one would hope for.
This is how a call for CGI variable is handled. eg <cfoutout>#cgi.THIS_IS_A_FAKE_KEY#</cfoutout>
The normal valid CGI scope variables are the ones in this list and these will be initialized to "" by default.
private static final String[] names ="AUTH_PASSWORD","AUTH_TYPE","AUTH_USER","CERT_COOKIE","CERT_FLAGS","CERT_ISSUER","CERT_KEYSIZE","CERT_SECRETKEYSIZE","CERT_SERIALNUMBER","CERT_SERVER_ISSUER","CERT_SERVER_SUBJECT","CERT_SUBJECT","CF_TEMPLATE_PATH","CONTENT_LENGTH","CONTENT_TYPE","CONTEXT_PATH","GATEWAY_INTERFACE","HTTP_ACCEPT","HTTP_ACCEPT_ENCODING","HTTP_ACCEPT_LANGUAGE","HTTP_CONNECTION","HTTP_COOKIE","HTTP_HOST","HTTP_USER_AGENT","HTTP_REFERER","HTTP_URL","HTTPS","HTTPS_KEYSIZE","HTTPS_SECRETKEYSIZE","HTTPS_SERVER_ISSUER","HTTPS_SERVER_SUBJECT","LOCAL_ADDR","PATH_INFO","PATH_TRANSLATED","QUERY_STRING","REMOTE_ADDR","REMOTE_HOST","REMOTE_USER","REQUEST_METHOD","SCRIPT_NAME","SERVER_NAME","SERVER_PORT","SERVER_PORT_SECURE","SERVER_PROTOCOL","SERVER_SOFTWARE","WEB_SERVER_API" };`
Also all of these values also come from various java libraries javax.servlet, HttpServletRequest etc.
If the requested variable is not any of these after a bit of checks, ColdFusion goes to the request headers. You can see these using getHttpRequestData().headers. Then looks for a key there with hyphens(-) instead of the underscores(_) in the cgi key request. (If the key starts with http_, then the key in the request headers will be with out it like this http_x_forward in request header will be x-forward)
value = request.getHeader(name.replace('_', '-'));
From what I understand as far as ColdFusion is concerned the keys, mentioned in the first point are the recognized as part of the CGI scope. But when there is additional information passed from a Apache load balancer server to ColdFusion, those end up in the request headers. Since the java getHeader just returns a blank string (or something with data type undefined) instead of a undefined error, ColdFusion does not identify any of the keys is defined or not.
So if the key THIS_IS_A_FAKE_KEY is sent to ColdFusion from an intermediary such as an Apache server. You will find that in the getHttpRequestData().headers['THIS-IS-A-FAKE-KEY'] but not on the CGI scope dump.
That being said my personal opinion is that it is better to check directly in the getHttpRequestData().headers for custom CGI variables other than in the scope itself.
EDIT Thanks to Ageax for pointing out one of my test cases was invalid on my earlier revision of this post.
Great bit of detective work RRK! So I decided to perform an experiment to verify your finding by creating two loops. The first loop will display the key/value pairs from getHttpRequestData().headers and the second loop does the same using the corresponding key/value pairs from the cgi scope by replacing the - with _. Voila! as reported by RRK, we can see how you can obtain the values by either method. I made an updated gist and posted here for anyone interested.
<cfset httpHeaders = getHttpRequestData().headers>
<h3>getHttpRequestData().headers</h3>
<cfloop collection="#httpHeaders#" item="key" >
<cfoutput><strong>#Key#</strong> : #httpHeaders[key]#<br></cfoutput>
</cfloop>
<h3>cgi keys dash to underscore</h3>
<cfloop collection="#httpHeaders#" item="key" >
<cfset keyUnderscore = replace(key, "-", "_", "all")>
<cfoutput><strong>#keyUnderscore#</strong> : #cgi[keyUnderscore]#<br></cfoutput>
</cfloop>
Does anyone know if its possible to invoke a fuseaction within a coldfusion template?
(You haven't specified which Fusebox version; this answer applies to Fusebox 5.x)
Your title and question is asking two different things - a fuse and a fuseaction are two distinct things. A fuse is simply a CFML template, whilst a fuseaction represents a bundle of logic that performs a particular action (similar to a function).
Fuses:
To invoke a fuse, simply include the file as you would normally - there's no special FB functionality required for this.
Fuseactions:
To invoke a fuseaction, use the do verb, like so:
<cfset myFusebox.do('circuit.fuseaction') />
To store the result, use the second argument for the content variable:
<cfset myFusebox.do('circuit.fuseaction',varname) />
This is the equivalent of this XML:
<do action="circuit.fuseaction" contentvariable="varname" />
There are other arguments available, see this Fusebox cheat sheet which contains plenty of other useful info too.
With MVC, you should be working through a single entry-point. So only a single fuseaction should be called during your request.
BUT that fuseaction can call some of the other model and view templates as needed. And I believe that Fusebox allows you to refactor that logic into something that can be used by multiple actions. (I'm a bit rusty on my Fusebox functionality though, but I bet some Googling will lead you the way.)
As a dire last resort, you could use <cfhttp> to call a URL within your app that invokes that fuseaction. But why not just run some of the code directly without needing to burden your server with another HTTP call?
I have in my cfm something like this
<CFModule name="MyModule"
someParam_one="#something.one#"
someParam_two="#something.two#"
someParam_etc="etc_etc_etc"/>
And inside my module, I have an
<CFSet param_name = "someParam_one">
...
evaluate("attributes." & param_name)
On most of our servers, this work. But on one of our servers, I get a
Error resolving parameter ATTRIBUTES.SOMEPARAM_NAME
Any ideas why?
Thanks
Have you verified that someParam_one is actually getting created? I've found, for example, that if I do something like this:
<cfset foo = myObject.getSomething() />
and getSomething returns a void value or runs a Java function that doesn't return anything, that CF will choke on it. The variable will be "defined", or so the application seems to think, but attempting to access it will throw an error. So do the following to track down and catch the problem:
Dump your attributes scope to make sure that what you want is indeed actually there.
Run a StructKeyExists(Attributes, param_name) before attempting to access the variable.
Get rid of the evaluate, and instead use Attributes[param_name]
Tangential to your question, but Evaluate() is evil, and an unnecessary evil in this situation. You can write this instead, and it will be more clear, more secure, and faster:
<cfset param_name = "someParam_one">
...
<cfset param_value = Attributes[param_name]>
A shot in the dark:
There's a bug in CFMX where if you
make a CFMODULE call to a template (or
use custom tag) from within a CFC and
that tempate uses the CALLER scope to
return data, the data is never
available to the CFC function. This is
bug 51067 and it is related to the
VARIABLES scope bug, 45138.
Seen in the user comments in the CFMX 6 docs on CFMODULE.
Ok, we did something really stupid :-)
We had two set of these files deployed and one was updated while the other was not, thus the error.
Thanks for all your help.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
In the spirit of my other questions regarding "common programming mistakes ... to avoid"
What are some common programming mistakes for a ColdFusion programmer to avoid?
set <cffile> upload path to a web accessible, CF-enabled directory!!!
isStruct() before isObject() in a series of <cfif>'s expecting isStruct only catches struct (cfc component returns True from isStruct() as well)
no HtmlEditFormat() when displaying user-generated content (XSS)
forgot to add output=false on CFC methods
not using <cfqueryparam> inside <cfquery>
not scoping not-so-evident variables like cfquery name or loop index in a method
use <cfform> when all they need is plain-vanilla HTML <form>
forgot to UrlEncodedFormat() user-defined URL
use <cffeed> without sanitizing the content
trust isDate() too much (any number would return true)
expect string comparison to be case-sensitive (IS and EQ operators are case-insensitive)
sending strings "yes" or "no" to SerializeJSON() without appending a whitespace to preserve the string (otherwise SerializeJSON() or DeserializeJSON() will translate them to "true" and "false")
not putting singletons services in application scope
blindly create as much CFCs as one wants like one would do in JAVA
putting complex value/object into a list (can't, list is just a string of comma-seperated values)
writing functions that takes array as an argument and modify that array expecting that array will be modified (array in CFML is passed by value)
blindly changes access="remote" on a method and expect it to work (when remote proxy is generally more appropriate)
use a lot of WriteOutput() in cfscript when CFML is more appropriate
blindly uses IsDefined() when StructKeyExists() can generally do it more efficiently
blindly uses Iif() and De() without knowing they're as nasty as Evaluate()
update some code in onApplicationStart() and not seeing the difference on refresh (restart the app!)
<cfloop> or '' outside of <cfquery> causing multiple new query connections to be opened. 99% of the time it's better to have multiple statements inside of one cfquery to perform multiple actions, or to UNION data together.
hardcoding absolute path when ExpandPath() is generally better
forgot to turn on Unicode support in DSN (Unicode becomes '????')
not upgrading to the latest JRE and Hotfixes
misusing Client scope and blow up Windows registry...
uses depreciated/obsolete functions/features (i.e. flash form aka flex 1.x alpha, cftable, Verity full-text search, etc...)
passing CFCATCH to a function as argument type Struct (CFCATCH behaves like a Struct, but it is not. Just pass it as type 'Any').
Not reading CFC Best Practices from ColdBox wiki.
buying in the mindset of .ASP(X) or .JSP or [insert web technology] are always better.. ;)
not use PrecisionEvaluate() and getting all sort of floating point rounding error especially when calculating money.
Inappropriate use of #
SELECT *
Not scrubbing URL/form inputs
Debugging on in production environment (even if output is suppressed)
Shamelessly stealing Henry's formatting...
it is faster and more accurate to check explicit boolean instead of implied; use <cfif query.recordCount GT 0> instead of <cfif query.recordCount>
do not use evaluate(), de(), or iif()... ever. there is always a way around these slow functions
understand structures, keys, values and how to access query and structure data using array notation. (this will usually get around your need for evaluate())
do not use pound signs unless you are outputting data or otherwise creating a string (don't do this: myFunction(arg = #myVar#) )
read and understand the difference between THIS and VARIABLES scope in a CFC
avoid extreme overuse of <cfsilent> when you probably need to use a <cfcontent reset="true"> just before you begin your output (before doctype, xml declaration, or <html>)
don't blindly drop ColdFusion values into an HTML script block (javascript) without using jsStringFormat()
if you are not using <CDATA> text in your XML, you may want to use xmlFormat() when creating an XML document
don't use Windows registry for Client scope data. Use the database.
if your IT architecture permits, use Session data instead of Client data.
use <cflock> correctly and consistently; shared data will leak in your Application.
if you are going to use Java objects, understand the Java error messages (for example, 'method cannot be found' may not mean the method does not exist at all, it means the method does not exist for the arguments you've supplied)
if you have to read large files, either use the new CF8 "File" functions or hand off the task to Java on CF6 & 7. <cffile> is inefficient for large files.
understand pass-by-reference and pass-by-value, and how those concepts work in CF; specifically when using functions to modify XML documents
as Henry stated, always use <cfqueryparam>; also ensure that you are using the correct CFSQLType parameter for your DBMS (for date, time, timestamp, etc)
don't chain together a series of <cfif> and <cfelseif> logic blocks, use <cfswitch> and <cfcase> if you have more than three conditions you need to handle
more of an architecture note: always do some sort of server side validation to catch the nasty data the wolf-shirt-wearing user may be passing you
last architecture note: let CF do your middle layer of data retrieval and display and let your webserver do webserver things like SEO URLs (I'm looking at you ColdCourse)
SQL Injection Attacks. It seems like cfquery is just made to allow them. So you should use cfqueryparams.
In Coldfusion, all variables are global by default, unless they are declared with the var keyword. (Somewhat similar to the situation in Javascript.)
So you either have to remember to var every variable used in a function, including things like names that are using in a cfquery name, or you can just use this trick:
<cffunction name="MyFunction">
<cfset var Local = StructNew()>
<!--- Now anything Local. is automatically local --->
<cfset Local.x = 42>
<!--- Including cfquery name="" --->
<cfquery name="Local.Customers" datasource="some_datasource">
SELECT C.ID, C.Name
FROM Customers C
</cfquery>
</cffunction>
There's nothing magic about the name Local, it's just convention. Although Coldfusion 9 will add an explict local scope, so if you use Local it will probably ease upgrading to CF9 when the time comes.
Note that the situation is a tad different for CFCs: In CFCs, the variables scope (the "default" scope) isn't global like it is for normal functions but rather exists per instance of your CFC. So while forgetting to use var is not quite as dangerous in a CFC as it is in a top-level function, the best practice is still to use var all the time.
Failing to prevent users from seeing coldfusion errors.
Add a onError method to a top level Application.cfc to prevent users from seeing those all to detailed dump messages exposing your inner workings(and failings).
<cffunction name="onError" returntype="void" output="true">
<cfargument name="exception" type="any" required="true" />
<cfargument name="eventname" type="string" required="true" />
varscoper is also a great tool for automating the check for variable scoping omissions in components.
http://varscoper.riaforge.org/
Overuse of 'query of query'. That is, further filtering or sorting of query results using the cfquery tag.
This type of work is often done better by the database itself, particularly if the dataset is large.
One of the biggest mistakes would be not using cfqueryparam
Very bad:
SELECT UserName
FROM Customers
WHERE CustomerID = #URL.custid#
Very good:
SELECT UserName
FROM Customers
WHERE CustomerID = <cfqueryparam value="#URL.custid#" cfsqltype="cf_sql_integer">`
Making that mistake will cost you a website.
Putting variables in the wrong scope; even if you don't blow up the registry or crash the server, it's easy to slowly drain performance from your application by bumping variables up to the highest scope in which you think you might need them, or to lose information because you stored it in one scope and tried to access them in a different scope.
Using cfcatch without capturing and/or transmitting some information about the error so that it can be found and fixed. (It's difficult to find an error that doesn't tell you it occurred.)
Using listcontains() when you want listfind(). Especially if the list contains numbers. listfind() matches only an entire item in a list; listcontains() matches part of an item. (Yes, we made this mistake once.)
With administrator access:
Leaving the defaults for a data source set up on the server. "Least privileges" applies on the CF side as well; don't give it any more permissions than it specifically needs. (GRANT, ALTER, REVOKE, DROP ... you don't really want those checked.)
Not checking the boxes for retrieving all the contents from a CLOB/BLOB field when that's what you're expecting. (It was really interesting seeing that applied to a field in which we were storing PDFs.)
Each time when using cfparam I have a kind of feeling that I put it in the wrong place or generally mis-using.
Consider this example, say we need to show the new entity form (with default input values):
<cfparam name="form.attachLink" default="" />
<input type="text" name="attachLink" value="#HTMLEditFormat(form.attachLink)#" />
Even this simple one makes me thinking about following questions on cfparam:
Should I use it before exact usage place? Wont it break the Model/View idea?
Or should I group them somewhere in model template, or maybe on top of the view?
When paramin' the form data, maybe it's better to use StructKeyExists(form, "attachLink")? Sure, I do this (plus validation) when processing the submitted form, but for the fresh form this can be useful too -- for safety-paranoid people like me.
Where else it makes sense to use this tag? I know one really useful place: custom tags, though they itself become more and more legacy too.
Thanks.
At the risk of sounding unhelpful: "It depends."
I think it's a bit complicated by the fact that CFPARAM can be used for setting default values as well as validating the type of a variable and throwing an error when it doesn't match. That said, I almost always use it just for the former. With the latter, a lot of that has been subsumed by arguments to components.
One of the more useful uses is on the action page for a form with checkboxes.
<cfparam name="form.myCheckbox" default="" />
Since it's valid for none of the checkboxes to be checked on the form, this prevents me from having to create special validation to see if the form variable exists before using it and, since I'm almost always treating it as a list, an empty-string is still valid for list functions.
As for where to place them, when I use them I almost always put them at the top of the cfm file, but that's probably just a style thing. If you sprinkle them in your code I think you're going to run into cases where the variable is going to be set and you don't know where it happened.
Of course, I'm using Model-Glue pretty much exclusively these days. Not much use for CFPARAM.
If you are going to reference the variable in a form value, you should use cfparam with a reasonable default (even blank) to ensure that the variable exists. However, if the variable is only being referenced upon form processing, I would skip cfparam (unnecessary as you're not setting a value in the form) and instead use structKeyExists(scope, "key") in the form processing step.
In an MVC framework, you may have an option to set your default values in another manner. In this case, you are most likely not referencing the form scope directly, but rather a framework managed scope. Fusebox uses the 'event' scope. On page load, the Fusebox framework merges both the URL and FORM scope together into the EVENT scope. If you are concerned about 'breaking MVC', I recommend setting up, and/or testing for the necessary variables in your framework's scope (in my case EVENT) in the MVC controller (using your framework's recommended method) before you display the form in the view. Most likely you won't need cfparam for this.
I think of cfparam the same way I think of variable declarations. I alway declare my variables as close as possible to where I'm going to use the variable. In coldfusion world, I use the cfparam tag right before the cfform tag.
It sounds like you're using an MVC framework. I don't see how this impacts the use of cfparam. The model is where you get the data. CFParam isn't specific to the model.