1

IN JAX-RS I need to iterate the (arbitrary) given query parameters in a request... but in their original order in the URI!

If I inject @Context UriInfo uriInfo then I can use uriInfo.getQueryParameters() to get a MultivaluedMap of the query parameters, grouped by the query parameter name. But what if I care about the original order of all the query parameters? Is there a way to simply iterate the name/value pairs? Or must I extract them manually from uriInfo.getRequestUri()?

If I'm stuck with manual extraction, is there some standard or well-maintained and updated library I can use for doing this?

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
  • 1
    For users saying that "you are not supposed to care about ordering", there's an example where it's good to preserve ordering: An API where the user supplies query parameters, e.g. `/api?foo=1&bar=2`, and the response for that returns an URL for getting the next page, e.g. `/api?foo=1&bar=2&page=2`. Having the query params not be randomly ordered makes it nicer. – robinst Jan 12 '18 at 00:30

3 Answers3

1

Query parameters are transformed to unordered Map (in jax-rs MultivaluedMap) in jax-rs and also in other REST/web frameworks. Also libraries, which are parsing the params are returning them in unordered way. I think for this special case you need to implement your own parsing solutions, which will put the params to LinkedHashMap. Look at existing custom parsing solutions here.

You can retrieve your query string with (mind that this is already decoded):

uriInfo.getRequestUri().getQuery()

However as you maybe know, your solution shouldn't rely on order of query parameters.

Community
  • 1
  • 1
Tomas Bartalos
  • 1,256
  • 12
  • 29
  • I am aware that most solutions do not rely on the order of query parameters. But can you provide a reference to your statement that a solution _should not_ rely on the order of query parameters? – Garret Wilson Sep 10 '14 at 14:28
  • Well, framework APIs are references itself. Jax-rs will return you MultivaluedMap, java servlet will return you Map, grails will return you GrailsParameterMap. Can you rely on order of elements in any of them? Massively used frameworks are not offering you a clean way of doing it, thus imho you shouldn't do it. Can you explain your use case ? – Tomas Bartalos Sep 10 '14 at 14:42
  • Framework APIs are references... of their individual domains. It is the URI specification that tells me if this is allowed in general, and I see nothing in RFC 3986 saying that query parameter order _must_ be insignificant. In fact RFC 3986 seems to imply that a query is not even required to carry name-value pairs. See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-uri-request.html for an example API in which order is useful (specifying multiple, order-significant sortings). – Garret Wilson Sep 10 '14 at 14:59
  • I get your point and in a sense you're right, RFC doesn't states, that the order is not preserved, so you are free to provide your implementation, caring the order. However we live in a world, where we don't like to reinvent the wheel. This is why we use APIs. And they don't offer you a clean way to do it (for example, look at jetty source code, they use unordered MultiMap, same with jersey's com.sun.jersey.core.util.MultivaluedMapImp). **And think about your clients, which will need to have special handling for your api, to preserve the order**. Increased complexicity and what is the benefit? – Tomas Bartalos Sep 10 '14 at 20:11
  • One more thing to be added. You didn't state which implementation of jax-rs api are you using. The order retention depends on the implementation of the MultivaluedMap class, which is provided by the implementing framework. – Tomas Bartalos Sep 10 '14 at 20:26
  • I don't want to do anything that depends on which implementation of JAX-RS I'm using. And I don't see what this has to do with clients. The JAX-RS client URI builder preserves order of query parameters, doesn't it? – Garret Wilson Sep 10 '14 at 20:34
  • Exactly, on one hand you don't want to depend on implementation (which is perfectly OK), but on the other hand the ordering depends on the implementation of MultivaluedMap inteface. This is why you can't and from perspective of jax-rs shouldn't rely on ordering (which was my original point). About the clients... 1) are you sure, that your clients will use jax-rs client builder? No interoperability? 2) Are you sure that all implementations of WebTarget interface preserve order of query params? You're enforcing constraints, which will cause troubles for clients of your api. – Tomas Bartalos Sep 10 '14 at 21:44
  • Sorry, @barti, but you're completely mixing apples and oranges. Just because one API method loses the ordering doesn't mean there wasn't an ordering to begin with. That's like saying, since `URI.getQuery()` loses an information regarding original encoding, you should never rely on encoding! Nonsense. URIs support encoding, and I can use it if I want---and I still don't have to depend on any implementation specifics. Besides we're way off subject. If you know of a library to get me the query parameters back in order, let me know; otherwise, I'll do it myself manually. – Garret Wilson Sep 10 '14 at 22:51
  • Your analogy is wrong, I can inject "@Context HttpServletRequest req" and then issue "req.getQueryString()", and I get the undecoded query string with no effort. What you're doing is unusual, but I let it live. To get back on track, for parsing you can use URLEncodedUtils form apache httpClient lib "parse(String s, Charset charset)", and use the returned "List" in ordered manner. – Tomas Bartalos Sep 11 '14 at 08:48
1

Custom solution with apache HttpClient library:

public void process(@Context UriInfo uriInfo) {
    String queryString = uriInfo.getRequestUri().getQuery()
    //TODO: extract the charset from Content-Type header, if present
    List<NameValuePair> queryParams = URLEncodedUtils.parse(queryString, "UTF-8")
    for(NameValuePair param : queryParams) {
        //do what you need
    }
}
Tomas Bartalos
  • 1,256
  • 12
  • 29
0

In case you're also using spring-web, you could parse the query parameters using UriComponentsBuilder which preserves ordering:

String query = uriInfo.getRequestUri().getQuery();
UriComponents components = UriComponentsBuilder.newInstance().query(query).build();
// Ordered as they appeared in the query string
MultiValueMap<String, String> queryParams = components.getQueryParams();
robinst
  • 30,027
  • 10
  • 102
  • 108