Recently Ben Nadel posted a blog entry that was in reply to an earlier entry of mine. Both entries talk about passing arrays of data to the server via client side JavaScript. In the comments we got a bit side tracked into talking about how to handle multiple form fields with the same name. What do I mean? Consider a form that looks like this:
<form action="test4a.cfm" method="post">
<input type="text" name="name" value="Camden,Raymond">
<input type="text" name="name" value="Smith,John">
<input type="submit">
</form>
Notice that I have two fields with the name "name." This is kind of dumb, but I've seen it many times before. And while you may not ever code this, you may be working on code that listens for posts from remote services. Those services may choose to send their data like that and not leave you with much choice.
ColdFusion will nicely take those two form fields and combine them into one form.name value. ColdFusion will also nicely turn the two values into one - a list. However, look at my values. They both have commas already. When submitted, I end up with form.name being "Camden,Raymond,Smith,John." There is no way to tell what my data really was.
I did a quick test though and looked at how getHTTPRequestData saw the values. I began by dumping the form scope and dumping the result of getHTTPRequestData():
Notice that the form scope is as I described - a list of munged data that we can't use. The result of getHTTPRequestData, however, is more clear. Notice specifically the content value:
name=Camden%2CRaymond&name=Smith%2CJohn
We've got a list of URL encoded values. Most importantly - the list of values are separate. What this means is that we can quickly turn this into a proper array:
<cfset data = []>
<cfif len(req.content)>
<cfloop index="item" list="#req.content#" delimiters="&">
<cfset arrayAppend(data, urlDecode(listLast(item, "=")))>
</cfloop>
</cfif>
As you can see, I simply treat the value as a & delimited list. For each item, I get the value to the right and urlDecode it. I append this to an array and I end up with my two values. Obviously this assumes that everything in the list is a NAME value. That would not be true if there were more form fields. But I think this is enough to give you a good start if you do indeed need to work with form data of this type.
Archived Comments
CF and lists to the rescue. What kind of magic can't you do with lists ;-)
Hey Ray, This will not work when you submit a multipart form. getHTTPRequestData() returns an empty binary value in this situation. We posted a question on stack overflow to get this figured out. http://stackoverflow.com/qu...
You know what - I was worried about that. Thanks for the link.
I've had a bit more coffee now. So if I read Leigh's comment right - if you wanted to write the code so it was _really_ dynamic, you would basically need to do a IF based on the post type. Agreed? I'd probably think that in most cases, the code is going to be used to reply to a specific call, so you don't have to be _quite_ as dynamic.
In the past, I've actually added a function in onRequestStart that processed a form post.
One of the many things it did was it gave me the ability to use the input name "name[]" which produces an array in the form struct called "name".
I haven't used it recently (don't even think I have the code anymore) but it was very useless when I needed it.
I meant useful... not useless.... lol
I wouldn't mind going into the getPageContext() for something like this; but it seems unfortunate that for multipart form data, we have to rely on an undocumented method on the form scope. If I use undocumented features, It's typically out of efficiency, not necessity :(
In any case, good stuff Ray.
Is it undocumented? I thought his method was using the Servlet API, which -is- documented (afaik).
@Ray,
Oh sorry, I was referring to the FORM.getPartsArray() method, which I think is undocumented - not the getPageContext() method.
Helps if I read I guess. ;)
There -has- to be a way via getPageContext.
@Ray
I did an extensive exploration of this topic while getting it to work in the cfwheels ( http://cfwheels.org ) project. I did see a reference that there was a setting to allow getting the binary data from getHTTPRequestData() but the framework cannot assume that every developer would have access to this setting.
@Ray,
I agree - there has to be a way. Why does getPageContext() have to be so darned huge!?!
Well, it is kind of cool that Java gives you access to 100% of the Request/Response objects. It's one of those cases where, you won't need this crap 99% of the time, but when you do, thank goodness it is there.
Interesting article Ray. I think that there may be a simpler way of dealing with these situations without having to loop over the list, using the split method:
<!--- get the http request data --->
<cfset formResults = getHTTPRequestData()/>
<!--- urldecode the resulting list --->
<cfset decodedList = urldecode(formResults.content)/>
<!--- split the resulting list into an array --->
<cfset results = decodedList.split("&name=") />
<!--- so far so good. Problem is that the first entry in the array will be name=Camden,Raymond. Need to remove the name= from the first array item--->
<cfset results[1] = replace(results[1],"name=","") />
<!--- output the results --->
<cfset request.cfdumpinited = false />
<cfdump label="results" expand="true" var="#results#"/>
<cfabort />
regards,
larry
Anyone tried:
getPageContext().getRequest().getParameterMap();
For this stuff? Know if it works with the multi-part?
Never mind, I see it has been tried. Railo doesn't have the magic function in the form scope, so I guess it's java time.
FYI, with wheels we finally found that with Adobe CF there is a hidden method attached to the form scope called getPartsArray() that will work with multipart form. the only cavert is that it's only works with multipart forms. Railo has a getRaw() method on the form scope.
more info can be found on cfsearching: http://cfsearching.blogspot...
also you can check out the $multipartData() method in the wheels dispatcher: http://github.com/rip747/cf...