Twitter: raymondcamden


Address: Lafayette, LA, USA

Processing forms with duplicate field names

03-31-2010 8,212 views ColdFusion 17 Comments

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:

view plain print about
1<form action="test4a.cfm" method="post">
2<input type="text" name="name" value="Camden,Raymond">
3<input type="text" name="name" value="Smith,John">
4<input type="submit">
5</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:

view plain print about
1<cfset data = []>
2<cfif len(req.content)>
3    <cfloop index="item" list="#req.content#" delimiters="&">
4        <cfset arrayAppend(data, urlDecode(listLast(item, "=")))>        
5    </cfloop>
6</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.

17 Comments

  • Commented on 04-01-2010 at 3:43 AM
    CF and lists to the rescue. What kind of magic can't you do with lists ;-)
  • Commented on 04-01-2010 at 6:29 AM
    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/questions/2194442
  • Commented on 04-01-2010 at 6:30 AM
    You know what - I was worried about that. Thanks for the link.
  • Commented on 04-01-2010 at 8:31 AM
    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.
  • Andrew #
    Commented on 04-01-2010 at 8:34 AM
    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.
  • Andrew #
    Commented on 04-01-2010 at 8:35 AM
    I meant useful... not useless.... lol
  • Commented on 04-01-2010 at 11:29 AM
    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.
  • Commented on 04-01-2010 at 11:31 AM
    Is it undocumented? I thought his method was using the Servlet API, which -is- documented (afaik).
  • Commented on 04-01-2010 at 11:33 AM
    @Ray,

    Oh sorry, I was referring to the FORM.getPartsArray() method, which I think is undocumented - not the getPageContext() method.
  • Commented on 04-01-2010 at 12:58 PM
    Helps if I read I guess. ;)

    There -has- to be a way via getPageContext.
  • Commented on 04-01-2010 at 1: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.
  • Commented on 04-01-2010 at 1:57 PM
    @Ray,

    I agree - there has to be a way. Why does getPageContext() have to be so darned huge!?!
  • Commented on 04-01-2010 at 1: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.
  • Larry C. Lyons #
    Commented on 04-02-2010 at 12: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
  • denstar #
    Commented on 06-05-2010 at 10:59 PM
    Anyone tried:

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

    For this stuff? Know if it works with the multi-part?
  • denstar #
    Commented on 06-05-2010 at 11:04 PM
    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.
  • Commented on 06-15-2010 at 6:41 PM
    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.com/2010/02/form-field...

    also you can check out the $multipartData() method in the wheels dispatcher: http://github.com/rip747/cfwheels/blob/master/whee...

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty