ColdFusion Application.cfc Wish - OnCommitStart() Event Method
Posted June 13, 2007 at 12:15 PM by Ben Nadel
Last night at the New York ColdFusion User Group, Michael Dinowitz was talking about Application.cfc and what features were added in ColdFusion 8. Lots of good stuff that I am very excited about. After the presentation, I was telling him that I would like one more event method to be available in the Application.cfc:
The idea is that this method would fire right before the page headers were flushed to the browser (signifying that page content was following close behind). I figure it would look pretty much just like the Application.cfc OnRequestStart() method:
- hint="Fires right before headers are flushed to the client.">
- <!--- Define arguments. --->
- <!--- Return out. --->
- <cfreturn true />
Now, you might be thinking, why the hell would anyone use this? Why not just use the OnRequestStart() or put something in your header includes? I am not saying that this is the most essential method, but there are some things that I think you could use it for. Before I explain, let's take a quick look at the page event method life cycle:
That's how it exists right now. But more than just the event chain, let's expand this to include the server-client communication life cycle as well (I am assuming that it is poor form to flush to the client from within OnRequestStart() since template execution decisions have not yet been made):
- -- Pre-page processing
- -- Pre-page-flush processing
- -- Content processing / flushing
- -- Final content flushing
Maybe my break down here is not correct, but to me, OnRequest() seems to cover two distinct parts of the page process. I am not saying that one of those two sub-steps should be moved to its own Application.cfc event method - that simply wouldn't make sense since both sub-steps are determined by the ColdFusion template you included in the OnRequest() method. I am saying that in between those two sub-steps, there should be one more application level event:
- -- Pre-page processing
- -- Pre-page-flush processing
- -- [ Hook into OnCommitStart() ]
- -- Content processing / flushing
- -- Final content flushed
Ok, so why would I even bother doing this? Again, not the most important functions, but here are some things that might be useful:
- Notice that OnCommitStart() returns a boolean. Like OnRequestStart(), a "false" return here would signal the page to halt processing and not let content be flushed. There might be information presented in the processing template that was not available at the point of OnRequestStart() that would determine how the page should be rendered (in a centralized fashion).
- This would give you the ability to update the FORM scope to prepare it for display. For example, most form-submission-validation cycles output FORM data back into the HTML form (ex. <input value="#FORM.name#" />). If FORM.name contains a quote, this would break the HTML (closing the attribute prematurely). This would be a good place to replace quote characters in the FORM scope with ". Of course, you don't want to do this before page processing as quote is a valid database entry, but once you have validated the FORM data and know you are going to display the page again - the quote character has no place being there.
- This would give you the opportunity to set flags regarding the page commitment (ie. you can no longer set page headers / cookies, etc.).
- This would be a perfect place to reset the content buffer to remove unnecessary white space.
- This would give you an opportunity to have content-specific CFSetting tags. For example, you might have EnableCFOutputOnly=true in the Application.cfc to kill extra white space, but then right before the content gets flushed, you can switch to EnableCFOutputOnly=false so that you don't have to wrap your entire HTML in CFOutput tags.
That's just off the top of my head; my gut tells me that this could be even more useful.
Now, you might look at that list and just say that all of that stuff can be accomplished already by putting tags and functions and directives in various other templates that get executed. Take for example the idea of resetting the content buffer - I could accomplish this by putting a CFContent tag (reset = true) at the top of my header files. Yes, I could do that, but that means I have to put that at the top of every single header template. Now, granted, I don't have a 100 different header templates to deal with, but if I have even more than one (ex. standard vs. print), why duplicate it at all? You could just put content reset in the OnCommitStart() and then every header you ever make will be able to execute without worrying about extra white space.
Am I crazy? Thoughts? Counter arguments?
That would come into my bottom 10% of all things i'd like to see in CF.
I am fine with that, but out of curiosity, is it bottom 10% because the ideas discussed are not a priority OR bottom 10% because they simply don't make any sense?
What kind of smart alec question is that Ben? Its obviously because it makes no sense whatsoever! ..Sheesh wasting our time and the Adobe CF developers as well. :) Did Michael Dinowitz post his presentation files online by any chance??
Dale, nothing crazy. All is explained
I'm not quite sure how I feel about it. On one hand it seems like it could provide some useful functionality (as described, and I've thought of a couple other uses).
But, on the other hand something just doesn't smell right about it. Now I could be off on this, but it seems like it would create some "gotchas" for some frameworks out there. I use Mach-ii for some projects I manage, and I'll occasionally toss a cfheader in some views that can be cached for an extended period of time. I also have a catch-all for events that don't exist and I'll put a cfheader with a 301 status code and re-direct to the default event.
But then again, I know for sure I'm crazy. ;)
Hrmm, on a side note would CF toss an error if you try to use cfheader after OnCommitStart() is ran (similar to cfflush)? Or would they change cfheader to be more like cfsetting and have it affect the whole request?
Check out http://www.nycfug.org/ If Michael posts his stuff, it will be there. But no promises.
I am like 99% sure that if you try to set a CFHeader value after the headers have been flushed, ColdFusion will throw an error. Check this out:
I actually used the ColdFusion error to determine this (Before I learned how to do it via GetPageContext()).
Yeah I knew that CF would throw an error if you used cfheader after cfflush. The same is true for cfhtmlhead.
OTOH, I suppose you might still be able to use cfhtmlhead after OnCommitStart() since it would be before the html head was sent, but after the http header was sent.
I guess you would just have to be careful about what sort of processing you were trying to preform inside OnCommitStart(), and make sure you don't place cfheader inside any templates. But! then again maybe I've just got a case of oral diarrhea (hey it happens...). :)
To be honest, I would advise against CFHtmlHead. I have only found that it causes more confusion than benefits. But, my experience is probably different than other people's.
It makes sense and I understand where you could use it.
But I can't see myself ever using it and have never come across a scenario where I thought, I wish I has something here. Thus bottom 10%
I'd much rather see better OO features or complete cfscript support
I am not getting what function would be performed by an OnCommitStartEvent(). We could certainly build one in but need to know exactly what the event is so we can tell when to call the method. Could you give me a little more on that request?
Basically, at some point, ColdFusion begins to flush the generated content to the browser. This can either be done automatically, or via the use of a CFFlush tag. At that point, the response is considered "committed", meaning that the headers have been flushed and they can no longer be set. But really, to me, this represents the start "page content" streaming.
I figured it would be cool to have a hook into this.
I had some example of things I could do, but as my style of programming has evolved, I am not sure that all of this would be necessary anymore. But, at the time, I definitely saw some use for it.
Anyway, don't spend too much time thinking about this as I don't think m(any) people would see value in it.
There is an OnPageEnd() method that gets called after processing and mark-up has been processed. I was thinking of using this for logging and stuff like that at this time. If you come up with additional use case scenario let me know and we will see how it works in.
Perhaps you might be interested in our sosContent.cfc as a way to do the stuff you were thinking of for the onCommitStart() method. The CFC is up on RiaForge also. :) It is a big key to achieving success in our ICE libraries without conflict.
I am gonna take a closer look at your stuff in general. At this point, I have really only watched presentations and seen blog posts. Thanks.