Mario
Mario

Reputation: 2505

MultivaluedMap in RESTEasy and square brackets

I'm building something of a relay server, using JAX-RS. I need to be able to extract any query parameters from a GET request and then re-wrap them into another request, to pass along to another server. I'm unfamiliar with MultivaluedMap, but I just figured out what is happening. The UriInfo class's getQueryParameters method returns a MultivaluedMap<String, String>. What bit me, unexpectedly, is that the values of each parameter are List values, even though they purport to be String values (by the way I read the JavaDoc).

In other words, if I have a key-value pair of foo=bar, in the URL query string, when I extract the parameter, it comes out as foo=[bar]. This totally throws me for a loop (a 500 Server Error, actually) when I try to re-wrap the parameter and send it along to the other server.

Is there another way to handle unpacking the query string from a Request, and then re-packing it for another Request? I'm including some code to illustrate my issue:

@GET
@Path("parameters")
public Response showParameters(@Context UriInfo uriInfo) {
    
    MultivaluedMap<String, String> parameters = uriInfo.getQueryParameters();
    
    StringBuffer sb = new StringBuffer();
    
    sb.append("<h4>Parameters:</h4>");
    if (parameters != null) {
        sb.append("<ul>");
        Iterator it = parameters.keySet().iterator();
        while (it.hasNext()) {
            String key = (String)it.next();
            sb.append("<li>")
                .append(key)
                .append(": ")
                .append(parameters.get(key))
                .append("</li>");
        }
        sb.append("</ul>");
    }
    else {
        sb.append("<p>None</p>");   
    }
    
    return Response.ok(sb.toString()).build();
}

So, in summary, what gets printed out from the code above, if the request has query parameters, is something like this:

Parameters:

Is there another way to unpack/re-pack, and maybe avoid this whole issue altogether? Thanks.

Complete answer

@Jack deserves credit for pointing me in the right direction, and I am marking his answer as correct, but here is what I got working.

    Client client = ClientBuilder.newClient();
    
    // Assume instance variable providing URI (without query string).
    WebTarget target = client.target(theRequestUri);
    
    // Instance variable uriInfo.
    MultivaluedMap<String, String> parameters = uriInfo.getQueryParameters();
    
    if (parameters != null && parameters.size() > 0) {
        Iterator it = parameters.keySet().iterator();
        String key = null;
        StringTokenizer st = null;
        
        while (it.hasNext()) {
            key = (String)it.next();
            
            // RESTEasy API is quirky, here. It wraps the values in square 
            // brackets; moreover, each value is separated by a comma and  
            // padded by a space as well. ([one, two, three, etc])
            
            st = new StringTokenizer(parameters.get(key).toString(), "[,]");
            while (st.hasMoreTokens()) {
                target = target.queryParam(key, st.nextToken().trim());
            }
        }
    }
    
    // Instance method: getContentType.
    Response response = target.request().accept(getContentType()).get();

Upvotes: 3

Views: 6381

Answers (1)

suman j
suman j

Reputation: 6960

Its because of MultivaluedMap interface.

public interface MultivaluedMap<K, V> extends Map<K, List<V>> {

It returns the values as List.
Instead of parameters.get(key) try parameters.getFirst(key)

Note that this will drop the other values for the same parameter. It is possible to send multiple values for same parameter while making a rest call such as blahblah:8080?foo=bar1&foo=bar2. With getFirst() call you will get bar1 value only. If you are sure you will not get multiple calls, you can go with getFirst(key) approach

--- UPDATED --- Based on your comments, it seems you need to iterate over the multivalued map and call queryParam on the WebTarget instance. I understand you are looking for a library/straight forward way to do this. I did not try RESTEasy. But code I believe should be simple enough.

multiValuesMap?.each { key, values->
 webTargetInstance = webTargetInstance.queryParam(key, values as Object[])
}

Upvotes: 3

Related Questions