I am using ColdFusion 9.0.1
I am running a page that will include other pages. The main purpose of this page is to determine which page to include. The pages that will be included will make use of the variable that I use in the page, which will be the MovieID (in this case).
My question is which is the best scope to use on this page? Would it be VARIABLES?
<cfscript>
Lookup = CGI.QUERY_STRING;
Query = CFC.doLookup(Lookup);
if (Query.RecordCount == 0) {
include "Content/Home.cfm";
} else {
// MOVIE PAGE
MovieID = Query.MovieID; // WHAT SCOPE SHOULD I USE HERE? FOR MOVIEID?
include "Content/Movie.cfm";
}
</cfscript>
Yes:
Variables
Maybe:
Application, Cookie, Request, Server, Session
No:
Arguments, Attributes, Caller, CGI, Client, Flash, Form, Local, This
ThisTag, Thread, thread local, URL
For info about each scope, check the CF9 documentation: scope types.
Sounds like you're implementing a Front Controller pattern.
Implement an Application.cfc, with an onRequest() that runs your code snippet. That way, it would intercept all incoming requests and include the appropriate cfm. Skipping the need for a handler index.cfm which simply includes the other cfm templates.
A further optimisation. Have your doLookup() method return a Struct containing just the recordCount + movieId as it appears that is all you are using. Returning a whole query is slower than just a struct of two numbers.
CF9 documentation: Application.cfc.
Variables (which is what you have now) certainly works. Another reasonable option would be Request, but I would lean toward variables over request.
Although either Variables scope or Request scope will be available to an included page, using Request scope could be helpful inasmuch as it is a scope that is intended to be available to any file in the request, whereas Variables scope is generally perceived to be specific to a given page.
In fact, Request scope was originally introduced for Allaire Spectra for the purpose of sharing variables across custom tags.
So, while both will do what you want, I would say that Request scope may have a small advantage of clarifying intent just a bit more.
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>
I am trying to create global functions in Lucee. I have followed the directions here and have it "Kind of" working. Inside of Application.cfc I placed the following code:
public void function onRequest() {
URL.IsInternalUser = function() { return (SESSION.user.ID ?: 0); };
}
This seems to work fine in some cases but if we need to access this function in a subdirectory that contains an Application.cfc that does not have the function re-defined it will error out saying the function doesn't exist.
The ultimate goal I am trying to achive is to have a cfc file that contains several user defined functions and then have them accessible throughout the entire application without redefining things over and over again.
Is there a better way to accomplish what we want? I ask this because on the page I referenced earlier in the comments section there is a quote:
I concur. Wouldn't surprise me to see it somewhere else in the future.
What I like about Railo's method is that it is completely sandboxed.
Host A's tags & functions libraries never cross/conflict with Host B's
libraries unless they're put into the global server folders. It's the
same way for the virtual file system too and pretty much everything
else (datasources, etc).
It specifically mentions Railo having a way to create UDF but I can not find any documentaion on this anywhere. Since we are using Lucee which is a fork of Railo I figure it must have what Railo has for creating UDF. Hoping someone that reads this can help me out and point me in the right direction.
Railo/Lucee support custom functions. You can declare them in /WEB-INF/{railo|lucee}/library/function/ of the site. Save the function in a .cfm file and name the file the same as the function. The server needs a restart after creating new functions. Here is Railo's blog post about it.
Adobe ColdFusion doesn't support this AFAIK. So you have to store your functions in the SERVER scope here.
I'm running my applications on CF 9. I created a CFC to concentrate my cookie handling instead of all the tags strewn about. It is simple. It has two attributes (name, value), and has 5 methods (setCookie, deleteCookie, verifyCookie, clearAllCookies, and init).
Since I wanted this method to be available throughout the application, I put this code in the onApplicationStart method of my application.cfc file:
application.oCookie = createObject("com.mycookie").init();
When I need to set a cookie in any code file I just call it like so:
application.oCookie.name="testCookieName";
application.oCookie.value="testCookieValue";
application.oCookie.setCookie();
My question is: Do I need to put a lock on this code each time I do this? If two separate users were to be on pages accessing this code at the same exact instant, can I end up with mixed up cookie values being set?
To make your oCookie thread-safe, it has to be a singleton (with no state) that only acts as a thin layer to the <cfcookie> or the cookie scope.
Therefore you should design your com.mycookie so that it accepts application.oCookie.setCookie(name, value) instead. And make sure you var-scope everything and don't store anything in the variables scope of mycookie.
And I don't think you need to use cflock.
If you haven't already, you may want to checkout WireBox.
I'm just starting out with ColdFusion OOP and I am wanting to make a DIV which shows different links to users depending on what page they are on and what login rights (role) they have. Basically a 'context' menu.
Should I put this toolbar/navigation DIV in a .cfm or .cfc file?
To reiterate; The cfm or cfc file needs to know what page the user is on and will also check what role they have. Depending on these two pieces of information it will display a set of links to the user. The role information comes from the database and stored in a SESSION variable, and to find out what page they are on I guess it could use #GetFileFromPath(GetBaseTemplatePath())#.
My first thought was to have a normal .cfm file, put all the presentation and logic in that file (the HTML and lots of <cfif> statements) to ensure the correct information is displayed in the DIV, and then use <cfinclude> to display it on the page. Then I started thinking maybe I should make a Custom Tag and ask the calling page to pass in the user's credentials and the #GetFileFromPath(GetBaseTemplatePath())# as arguments and then have that Custom Tag return all the presentational data.
Finally I guess a CFC could do the above as well, but I'd be breaking the 'rule' of having presentational and logic data in a CFC.
Any suggestions on the best practice to achieve what I'm trying to do? It will eventually serve thousands of customers so I need to make sure my solution is easy to scale.
Anything that outputs HTML to the screen should be in a .cfm file.
That being said, depending on your need, you could have methods in a CFC that generate HTML, but the method simply returns the HTML as a string.
In programming, there are very few absolutes, but here is one: You should NEVER directly output anything inside of a function or method by using output="true". Instead, whatever content is generated, it should be returned from the method.
If you will have a need to use this display element more than once, a custom tag might be the best way to go rather than an include.
I see security as being a combination of what menu items I can see and what pages can be ran.
The main security function is inside of the main session object
On the menus
I call a function called
if (session.objState.checkSecurity(Section, Item) == 1)
then ...
For page security
function setupRequest() {
...
if (session.objState.checkSecurity(getSection(), getItem()) == 0) {
location("#request.self#?message=LoginExpired", "no");
return;
}
...
}
The particulars of what checkSecurity can do varies from application to application, but it is tied into how FW/1 works. The following security variations exist:
session.objState.checkSecurity(getSection())
session.objState.checkSecurity(getSection(), getItem())
session.objState.checkSecurity(getSection(), getItem(), Identifier)
None of the presentation files know anything about security.
Rules by which I live:) :
No CF business logic in CFM files. Just use some service which will serve template and provide needed data.
navService = com.foobar.services.Navigation(form, url);
and later output #navService.GetNavConent()#
No direct output from CFC files, functions should always return content. For example, make one function which makes one link based on some logic, second which wraps that and returns to cfm template.
Also one more hint, avoid using application and session scopes in your services.
This makes refactoring, testing and debugging too difficult.
For session you can make session.currentUser , CurrentUser.cfc which provides all things you need. e.g. session.currentUser.isAuthorized("backend/administration") and if true, show link to backend/administration.
Same for application, if you need locale, applicaiton wide setting or some singleton, make application.applicationSettings, ApplicationSettings.cfc and use that to retrieve all info you need in cfc's.
These rules will make your application to be easier to test and debug, and really easy to migrate tomorrow on some javascript based UI like Angular or backbone.js since all th edata you need is already in CFC and theoretically you just need to put remote in CFC or make some remote facade in the middle and you're done.
This is more of a conceptual question. I have a working application that allows users to upload a CSV file of addresses, then parses the data into an Array of Address objects, then validates each Address object against certain rules (certain fields are required, etc.). The page then displays any addresses that failed validation, giving the user the ability to edit or delete each.
Right now, I am storing the entire Array in a SESSION variable, assigning each Address an Id value, then updating each Address in the SESSION Array when the user makes edits and submits the form.
I'm trying to think of a way to do this without using the SESSION scope, or using a physical database, or physical file. Any ideas?
If you don't use a physical database you would have to use some sort of persistent scope. That means the SESSION scope, the CLIENT scope (if you have that enabled), the APPLICATION scope, or the SERVER scope. But I think the safest way (as all those persistent scopes are cleared if your server goes down) is to store them in a database -- whether that database is a RDBMS, text file, or a Verity or Solr collection. I apologize in advance if that doesn't answer your question.
The data needs to be stored / preserved somewhere if you want to work with it across multiple requests. Aside from the options your question rules out (session, database, file), I can think of two other (non-ideal) options:
External cache mechanism like memcached -- not necessarily recommended because it's inherently volatile and doesn't guarantee to preserve your data
Pass the contents of the CSV around from request to request, e.g. via hidden FORM containing JSON -- not recommended if the CSV can get large
Personally, my colleagues and I tend to use temporary database storage for this type of issue.
I don't know anything about your requirements or use cases, but if you can depend on your users to have modern browsers, another viable option might be HTML5 localStorage.
Skimming some of the localstorage questions might give you some ideas.