playframework / play-ws

Standalone Play WS, an async HTTP client with fluent API
https://www.playframework.com/documentation/latest/JavaWS
Apache License 2.0
223 stars 86 forks source link

Order of Form Parameters is not preserved (application/x-www-form-urlencoded) #447

Open Marc2017 opened 4 years ago

Marc2017 commented 4 years ago

Play WS Version (2.7.2 / etc) JAVA

In StandaloneAhcWSRequest the order of Form-Parameters is not preserved:


                    // XXX shouldn't the encoding be same as charset?
                    Map<String, List<String>> stringListMap = FormUrlEncodedParser.parseAsJava(stringBody, "utf-8");
                    stringListMap.forEach((key, values) -> values.forEach(value -> builder.addFormParam(key, value)));

"FormUrlEncodedParser.parseAsJava" does not preserver the order, and "stringListMap" is unordered.

wsargent commented 4 years ago

So the call is from https://github.com/playframework/play-ws/blob/master/play-ahc-ws-standalone/src/main/java/play/libs/ws/ahc/StandaloneAhcWSRequest.java#L450

Looking at https://github.com/playframework/play-ws/blob/master/play-ahc-ws-standalone/src/main/scala/play/api/libs/ws/ahc/FormUrlEncodedParser.scala#L47 it certainly seems like parseAsJava is ordered.

Can you put together a test case?

wsargent commented 4 years ago

Or is it that the asJava method doesn't retain sorting order?

If you call

  def parseAsJava(data: String, encoding: String): java.util.Map[String, java.util.List[String]] = {
    import scala.collection.JavaConverters._
    val insertHashMap = new util.LinkedHashMap[String, java.util.List[String]]()
    parse(data, encoding).foreach {
      case (key, values) =>
        insertHashMap.put(key, values.asJava)
    }
    insertHashMap
  }

  def parseAsJavaArrayValues(data: String, encoding: String): java.util.Map[String, Array[String]] = {
    val insertHashMap = new util.LinkedHashMap[String, Array[String]]()
    parse(data, encoding).foreach {
      case (key, values) =>
        insertHashMap.put(key, values.toArray)
    }
    insertHashMap
  }

does that change things for you?

Marc2017 commented 4 years ago

Hi,

seems I have a different implementation:

def parseAsJava(data: String, encoding: String): java.util.Map[String, java.util.List[String]] = { import scala.collection.JavaConverters._ parse(data, encoding).map { case (key, values) => key -> values.asJava }.asJava }

there is no "foreach", there is a "map" - which will change the order.

I´ll check my revision.

Thanks! Marc

Marc2017 commented 4 years ago

Did you change it to "foreach" in your example? In the current revision there is also a "map".

wsargent commented 4 years ago

@Marc2017 yeah, the example above is using a foreach because it uses an insertion order linked hash map. It's not what's in the master branch.