3

Running ColdFusion 7.

The Application.cfm loops all session variables into request.session

The OnRequestEnd.cfm loops all request.session values back into session

It does this so it only needs to lock the scope once when writing the variables in a single transaction. (I believe this isn't so much an issue anymore? Yet I can't exactly rip it out).

I have a 'redirect.cfm' page which either provides a 301 redirect to an SEO URL or delivers the content. Some forms post to the old URL, so need a 301 redirect which causes the loss of POST data. This is how I intended to handle it.

<!--- if form scope exists (posted data) copy it to the request.session scope ---> 
<cfif structKeyExists(form,'fieldNames')>
    <cfset request.session.postData = structCopy(form)>
</cfif>

Then it moves on to a 301 redirect, and when it comes back to redirect.cfm to deliver the content it runs this code

<!--- if request.session.postData exists (posted data) copy it to the form scope --->
<cfif structKeyExists(request.session,'postData')>
    <cfset form = structCopy(request.session.postData)>
    <cfset StructDelete(request.session,'postData')>
</cfif>

This works fine if a 301 redirect is not needed from post of data.

With a 301 redirect, I have confirmed the Application.cfm, OnRequestEnd.cfm both run twice (once for the initial 301 and once for the content delivery).

By the end of the first OnRequestEnd.cfm call session.postdata is populated correctly with the form data.

After the 301 redirect and it hits Application.cfm again the session.postdata returns 'struct[empty]'

Any help? Thanks

James A Mohler
  • 11,060
  • 15
  • 46
  • 72
Daniel Cook
  • 1,033
  • 1
  • 9
  • 19
  • I tested another idea, I set request.session.test = form.searchField on a 301 redirect and it worked. It looks like the problem might be to do with storing a structure in a request.session variable and passing it. – Daniel Cook Dec 03 '12 at 16:15
  • A redirect is a new request so if you want your post data to exist in your next screen you need to add it to a persistent scope like session or pass it in the URL. – genericHCU Dec 03 '12 at 16:22
  • Hi Travis, if you see the code snippets in my post that is what I'm doing. I am copying the form to request.session before the 301 redirect, the onRequestEnd.cfm writes this to the Session scope. – Daniel Cook Dec 03 '12 at 16:30
  • have you tried placing it directly into the session scope without having to have it "looped in"? – genericHCU Dec 03 '12 at 16:39
  • What are you attempting to accomplish by copying session variables to the request scope? I can't think of anything useful. – Dan Bracuk Dec 03 '12 at 16:42
  • @Dan Bracuk, I think it's legacy code he's working with. Daniel, how does your in script convert the request.session to session. You can save a structure to the session scope, so that shouldn't be the problem. It sounds like your copying in and out of scopes is only handling simple values? – genericHCU Dec 03 '12 at 16:45
  • @Travis I think that's about right. I tested ` ` the simple 'postDataSubmit' survives in the Session, the structure does not. The loop code is like this ` ` – Daniel Cook Dec 03 '12 at 16:49
  • @DanBracuk Travis is right, it's legacy code - it's heavily integrated and I could not easily remove it. – Daniel Cook Dec 03 '12 at 16:50
  • @Travis furthermore, when stored directly into the session scope OnRequestEnd.cfm should not touch it. I checked the value entering the Application.cfm and session.postdata shows struct [empty]. Maybe I need to use something other than `structCopy(form)` – Daniel Cook Dec 03 '12 at 16:58
  • 1
    Oh of course... structcopy copies nested structures by reference, so when the form structure no longer exists, neither does your postdata structure. – genericHCU Dec 03 '12 at 17:05
  • 1
    try duplicate instead of structcopy – genericHCU Dec 03 '12 at 17:06
  • @Travis thanks a lot, that worked, write a quick note as an 'Answer' if you like and I'll mark it as such. Leaving work now, will do it in the morning. – Daniel Cook Dec 03 '12 at 17:13
  • done. sorry I didn't see it sooner. to many superfluous comments. – genericHCU Dec 03 '12 at 17:22

2 Answers2

5

structCopy() creates a shallow copy of the structure, meaning that nested structures are by reference only which is why your simple values persisted but your nested structures did not. Once your form structure no longer contained data your postData structure began referencing an empty structure so your reference is also empty.

To do a "Deep Copy" of your structures use duplicate()

See also other structure functions

CF 9 documentation for deleting structures

genericHCU
  • 4,394
  • 2
  • 22
  • 34
  • Even if what Adam says is correct about structCopy(), duplicate has resolved the issue. Thank you! – Daniel Cook Dec 03 '12 at 23:14
  • any thoughts why this works `` but when I do `` the form scope remains empty. This code is ran just before I `` the content. Same with structCopy() Thanks – Daniel Cook Dec 04 '12 at 09:42
  • I'm going with this until I find a better solution `` I feel so dirty.. – Daniel Cook Dec 04 '12 at 10:25
  • I'm sure it has something to do with form being a full scope and not just a structure, a reserved word type protection. you can say form.someName = request.session.postData just fine, but I believe CF has a fail safe to prevent using scopes as structure names. If I'm wrong someone will chime in but that's my best guess. if your landing page is already coded and looking at the form scope, I think you're on the right track. If you're coding the landing page, I see no value in moving your postData back into the form scope, just code to use the postData structure. Lots of bouncing around already. – genericHCU Dec 04 '12 at 11:14
  • thanks, I may have to stick to the loop. This is a blanket fix for all forms on the website. There are over 50,000 content pages (of course not all forms), but it's a pretty large site. – Daniel Cook Dec 04 '12 at 11:19
  • @Travis, you're dead right, and I'm sorry I disagreed with you here. I've done some more looking into this, which I have documented on my blog, should you be interested: http://adamcameroncoldfusion.blogspot.co.uk/2012/12/i-love-being-wrong.html – Adam Cameron Dec 05 '12 at 09:24
0

[UPDATE: this answer is wrong. I've only left it here as I've done some interesting investigation into the situation, which might be worth people reading]

What @Travis said was the problem - that "when the form structure no longer exists, neither does your postdata structure" - is absolutely not true. The reference to the form scope might have gone, but as long as there's any reference to the data (like request.session.postData) then the underlying object will not be removed.

The problem is likely to be that when you do your redirect, OnRequestEnd.cfm doesn't run for that request, so the variables in request.session never get copied into the session scope. It all depends on how you're doing the redirect. I'd've assumed it'd have been with a <cflocation>, except you say you're using CFMX7 which can't do a 301 (which you're say you're doing): it can only do a 302. Can you pls clarify how you're doing this redirect? Cheers.

Digression: you're adding quite a lot of overhead plus a bottleneck at either end of the request in continuing with all this legacy session-locking shenanigans. You really ought to put aside some time to pull it all out. This locking session-scope-locking carry-on hasn't been necessary since CF5. Also if your session scope contains structs, you're not mitigating the problem anyhow. This is, obviously, an aside; and nothing to do with your actual question. I just figured it was worth mentioning.

Adam Cameron
  • 29,677
  • 4
  • 37
  • 78
  • Application.cfm and OnRequestEnd.cfm both run twice, once for the 301 and once for the content. I am sure that OnRequestEnd.cfm ran. I even dumped the content of the session scope built by OnRequestEnd.cfm which contained the form data. Then I dumped the Session scope at the beginning of the following Application.cfm call - when using structCopy() the structure was empty. – Daniel Cook Dec 03 '12 at 23:17
  • I am using ` ` for the redirect and it seems to run fine in CF7. Whilst not ideal, it would be costly to refactor the website or preferable build it around a modern CMS - we do have a CDN which alleviates the strain on the server, – Daniel Cook Dec 03 '12 at 23:20
  • Yes, I misspoke, the object exists as Daniel indicated by saying struct[empty] so the struct is there or he would have gotten a not defined in request error. However the form scope is empty so the copy is empty. http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec09f0b-7fdc.html – genericHCU Dec 03 '12 at 23:27
  • did some testing that says contrary to your answer. Can you post your source(s)? a.user.name = "Travis"; b = structCopy(a); c=a; structDelete(a,'user'); a.user.name = "Adam"; b.user.name = "Mike" <--- changes B and C but not A ---> I suppose this suggests the original object exists but is no longer accessible by re-creating a structure with the same name as the originally deleted structure? – genericHCU Dec 04 '12 at 00:36