Processing forms with duplicate field names

This post is more than 2 years old.

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.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Sebastiaan posted on 4/1/2010 at 12:43 PM

CF and lists to the rescue. What kind of magic can't you do with lists ;-)

Comment 2 by James Gibson posted on 4/1/2010 at 3:29 PM

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...

Comment 3 by Raymond Camden posted on 4/1/2010 at 3:30 PM

You know what - I was worried about that. Thanks for the link.

Comment 4 by Raymond Camden posted on 4/1/2010 at 5:31 PM

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.

Comment 5 by Andrew posted on 4/1/2010 at 5:34 PM

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.

Comment 6 by Andrew posted on 4/1/2010 at 5:35 PM

I meant useful... not useless.... lol

Comment 7 by Ben Nadel posted on 4/1/2010 at 8:29 PM

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.

Comment 8 by Raymond Camden posted on 4/1/2010 at 8:31 PM

Is it undocumented? I thought his method was using the Servlet API, which -is- documented (afaik).

Comment 9 by Ben Nadel posted on 4/1/2010 at 8:33 PM

@Ray,

Oh sorry, I was referring to the FORM.getPartsArray() method, which I think is undocumented - not the getPageContext() method.

Comment 10 by Raymond Camden posted on 4/1/2010 at 9:58 PM

Helps if I read I guess. ;)

There -has- to be a way via getPageContext.

Comment 11 by James Gibson posted on 4/1/2010 at 10:14 PM

@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.

Comment 12 by Ben Nadel posted on 4/1/2010 at 10:57 PM

@Ray,

I agree - there has to be a way. Why does getPageContext() have to be so darned huge!?!

Comment 13 by Raymond Camden posted on 4/1/2010 at 10:59 PM

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.

Comment 14 by Larry C. Lyons posted on 4/2/2010 at 9:23 PM

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

Comment 15 by denstar posted on 6/6/2010 at 7:59 AM

Anyone tried:

getPageContext().getRequest().getParameterMap();

For this stuff? Know if it works with the multi-part?

Comment 16 by denstar posted on 6/6/2010 at 8:04 AM

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.

Comment 17 by tony petruzzi posted on 6/16/2010 at 3:41 AM

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...