Encoding curly braces in Jersey Client 2
Asked Answered
L

5

6

We are using Jersey Client 2.21. I am noticing that when we put curly braces (aka curly brackets) as a param value, then it does not get properly encoded. Not only that, but anything inside the curly braces does not get encoded either. This is not true for regular brackets or other unsafe characters that I have tested with.

Please see the example below. In this example I enter three params. A control param with just spaces. One with curly braces, and one with regular brackets.

public static void testJerseyEncoding() {
    Client client = ClientBuilder.newClient();
    String url = "http://foo.com/path";
    Map<String, String> map = new HashMap<>();
    map.put("paramWithCurly", " {with a space}");
    map.put("paramWithOutCurly", "with a space");
    map.put("paramWithBracket", "[with a space]");
    WebTarget target = client.target(url);
    for (Map.Entry<String, String> entry : map.entrySet()) {
        target = target.queryParam(entry.getKey(), entry.getValue());
    }
    System.out.println(target.toString());
}

Here is the output:

JerseyWebTarget { http://foo.com/path?paramWithBracket=%5Bwith+a+space%5D&paramWithOutCurly=with+a+space&paramWithCurly=+{with a space} }

Is something broken with the Jersey Client or am I missing something? The curly braces should have been encoded to "%7B".

Leucine answered 26/2, 2016 at 18:38 Comment(0)
I
2

When you create a parameter with a value in curly, Jersey thinks you want to use a URL parameter. See https://jersey.github.io/documentation/latest/uris-and-links.html.

UriBuilder.fromUri("http://localhost/")
 .path("{a}")
 .queryParam("name", "{value}")
 .build("segment", "value");

So you should encode curly braces yourselves via URLEncoder, probably as described there: How to force URIBuilder.path(...) to encode parameters like "%AD"? This method doesn't always encode parameters with percentage, correctly.

Iamb answered 26/2, 2016 at 18:48 Comment(1)
thank you for uncovering the mystery of why it did not encode the curly braces. any idea how to turn off this curly brace "feature"? Seems like a bad idea for the jersey client framework treating curly braces differently, curly braces are encodable and hence may show up in URI's.Leucine
C
9

Instead of manually pre-encoding the query parameter value, a better way might be do always use a template parameter and then use resolveTemplate() with the un-safe value.

Client client = ClientBuilder.newClient();

WebTarget target = client.target("http://server")
            .path("/foo")
            .queryParam("bar", "{bar}")
            .resolveTemplate("bar", "{\"foo\":\"bar\"}");

assertThat(target.getUri().toString())
        .isEqualTo("http://server/foo?bar=%7B%22foo%22%3A%22bar%22%7D");
Cafeteria answered 12/9, 2017 at 23:59 Comment(0)
S
9

So, first of all it's insane that Jersey is doing templating by default. Secondly, all the solutions here are wrong...doing URLEncoder.encode(..., "UTF-8") will not work for query params which contains spaces. Since the URLEncoder will encode the space as +, which Jersey will interpret as a plus sign, so Jersey ends up encoding it as %2B. See https://docs.oracle.com/javase/7/docs/api/java/net/URLEncoder.html for reference.

My proposed solution, which I'm not very happy with (as always with Java) is to replace all { and } with %7B and %7D respectively, as following:

Map<String, String> map = new HashMap<>();
map.put("paramWithCurly", " {with a space}".replaceAll("\\{", "%7B").replaceAll("\\}", "%7D"));
map.put("paramWithOutCurly", "with a space");
map.put("paramWithBracket", "[with a space]");
WebTarget target = client.target(url);
for (Map.Entry<String, String> entry : map.entrySet()) {
    target = target.queryParam(entry.getKey(), entry.getValue());
}
Siusan answered 25/10, 2018 at 13:0 Comment(1)
Escaping just curly braces is a nice trick. You saved my day !!!Tantalus
I
2

When you create a parameter with a value in curly, Jersey thinks you want to use a URL parameter. See https://jersey.github.io/documentation/latest/uris-and-links.html.

UriBuilder.fromUri("http://localhost/")
 .path("{a}")
 .queryParam("name", "{value}")
 .build("segment", "value");

So you should encode curly braces yourselves via URLEncoder, probably as described there: How to force URIBuilder.path(...) to encode parameters like "%AD"? This method doesn't always encode parameters with percentage, correctly.

Iamb answered 26/2, 2016 at 18:48 Comment(1)
thank you for uncovering the mystery of why it did not encode the curly braces. any idea how to turn off this curly brace "feature"? Seems like a bad idea for the jersey client framework treating curly braces differently, curly braces are encodable and hence may show up in URI's.Leucine
C
0

You could solve this problem by using URLEncoder.encode( "..." , "UTF-8") method

String java.net.URLEncoder.encode(String s, String enc) throws UnsupportedEncodingException

Translates a string into application/x-www-form-urlencoded format using a specific encoding scheme. This method uses the supplied encoding scheme to obtain the bytes for unsafe characters.

update your code by using URLEncoder.encode

    try { 
        map.put("paramWithCurly", URLEncoder.encode(" {with a space}", "UTF-8"));
        map.put("paramWithOutCurly", URLEncoder.encode("with a space", "UTF-8"));
        map.put("paramWithBracket", URLEncoder.encode("[with a space]", "UTF-8"));
    } catch (UnsupportedEncodingException e1) {
         System.err.println("........");
    }

Here is the output:

JerseyWebTarget { http://foo.com/path?paramWithBracket=%5Bwith%2Ba%2Bspace%5D&paramWithOutCurly=with%2Ba%2Bspace&paramWithCurly=%2B%7Bwith%2Ba%2Bspace%7D }

Hint:-

using UTF-8 as the encoding scheme the string "The string ü@foo-bar" would get converted to "The+string+%C3%BC%40foo-bar" 

Reference: https://mcmap.net/q/188595/-java-equivalent-to-javascript-39-s-encodeuricomponent-that-produces-identical-output

Copolymer answered 23/9, 2018 at 14:21 Comment(0)
S
-1

It seems that Karim's solution above will not work, exactly as predicted by Tapped. The spaces were encoded as plus signs by URLEncoder.encode which were then encoded by Jersey to %2B characters which is incorrect.

Sonorous answered 10/12, 2018 at 23:29 Comment(2)
This is not a real answer. What do you suggest to solve the issue ?Jamilajamill
Sorry. I used target = target.queryParam(entry.getKey(), UriComponent.encode(entry.getValue(), UriComponent.Type.QUERY_PARAM)); instead of target = target.queryParam(entry.getKey(), entry.getValue());Sonorous

© 2022 - 2024 — McMap. All rights reserved.