URL encoding using the new Spring UriComponentsBuilder - spring

I'm attempting to use spring's UriComponentsBuilder to generate some urls for oauth interaction. The query parameters include such entities as callback urls and parameter values with spaces in them.
Attempting to use UriComponentBuilder (because UriUtils is now deprecated)
UriComponentsBuilder urlBuilder = UriComponentsBuilder.fromHttpUrl(oauthURL);
urlBuilder.queryParam("client_id", clientId);
urlBuilder.queryParam("redirect_uri", redirectURI);
urlBuilder.queryParam("scope", "test1 test2");
String url = urlBuilder.build(false).encode().toUriString();
Unfortunately, while the space in the scope parameter is successfully replaced with '+', the redirect_uri parameter is not at all url encoded.
E.g,
redirect_uri=https://oauth2-login-demo.appspot.com/code
should have ended up
redirect_uri=https%3A%2F%2Foauth2-login-demo.appspot.com%2Fcode
but was untouched. Diving into the code, specifically org.springframework.web.util.HierarchicalUriComponents.Type.QUERY_PARAM.isAllowed(c) :
if ('=' == c || '+' == c || '&' == c) {
return false;
}
else {
return isPchar(c) || '/' == c || '?' == c;
}
clearly allows ':' and '/' characters, which by gum, it shouldn't. It must be doing some other type of encoding, though for the life of me, I can't imagine what. Am I barking up the wrong tree(s) here?
Thanks

UriComponentsBuilder is encoding your URI in accordance with RFC 3986, with section 3.4 about the 'query' component of a URI being of particular note.
Within the 'query' component, the characters / and : are permitted, and do not need escaping.
To take the / character for example: the 'query' component (which is clearly delimited by unescaped ? and (optionally) # characters), is not hierarchical and the / character has no special meaning. So it doesn't need encoding.

from what I understand, UriComponentsBuilder doesn't encode the query parameters automatically, just the original HttpUrl it's instantiated with. In other words, you still have to explicitly encode:
String redirectURI= "https://oauth2-login-demo.appspot.com/code";
urlBuilder.queryParam("redirect_uri", URLEncoder.encode(redirectURI,"UTF-8" ));

Try to scan the UriComponentsBuilder doc, there is method named build(boolean encoded)
Sample code 1:
UriComponents uriComponents = UriComponentsBuilder.fromPath("/path1/path2").build(true);
Here is my sample code 2:
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("https")
.host("my.host")
.path("/path1/path2").query(parameters).build(true);
URI uri= uriComponents.toUri();
ResponseEntity<MyEntityResponse> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, entity, typeRef);

I tried all the solutions above until I got it working.
In my example, I was trying to encode the ZonedDateTime format 2022-01-21T10:17:10.228+06:00. The plus sign was a problem.
What solved my issue was encoding the value manually + using URI instead of the string value (both were very important).
Before:
restTemplate.exchange(
UriComponentsBuilder
.queryParam("fromDateTime", "2022-01-21T10:17:10.228+06:00")
.build()
.toUriString(),
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MyDto>>() {}
);
After:
restTemplate.exchange(
UriComponentsBuilder
.queryParam("fromDateTime", URLEncoder.encode("2022-01-21T10:17:10.228+06:00", StandardCharsets.UTF_8))
.build(true)
.toUri(),
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MyDto>>() {}
);

Related

Can we have two #PathVariable. One as the actual Path Variable and other for swagger to document it as Deprecated?

I am trying to change the REST call's #PathVariable. The existing Path Variable is formed by combination of three parameters. The change is to handle Path Variable formed by combination of two parameters. I need this change to be documented in swagger with the earlier shown as Deprecated.
I have tried to use both Path Variable with one as #Deprecated like below
#Parameter(description = "x_y_z - x is first ID, y is second ID, z is third ID", required=false )
#Deprecated #PathVariable String x_y_z,
#Parameter(description = "x_y - x is first ID, y is second ID", required=true )
#PathVariable String x_y)
The have changed request mapping value from /aaa/bbb/{x_y_z} to below
#RequestMapping(value = "/aaa/bbb/{x_y}", method = RequestMethod.GET, produces = "application/json"
With above changes the request fails with 500 error, may be since it is expecting two Path Variables. But the swagger documentation is as expected.
I tried to remove #PathVariable for x_y_z. The request is processed as expected and the swagger now shows x_y_z as deprecated but shows the parameter as (query) instead of (path)
Any suggestions please
Assuming an #RestController and that Swagger understands #Deprecated for a method:
#Deprecated
#GetMapping("/aaa/bbb/{x:\\d+}_{y:\\d+}_{z:\\d+}")
public ResponseEntity<MessageResponse> getStuff(#PathVariable String x,
#PathVariable String y,
#PathVariable(name = "z", required = false) String z) {
return getNewStuff(x, y); //send to the other method and ignore z
}
#GetMapping("/aaa/bbb/{x:\\d+}_{y:\\d+}")
public ResponseEntity<MessageResponse> getNewStuff(#PathVariable String x,
#PathVariable String y) {
// do stuff for x and y by default
return ResponseEntity.ok(new MessageResponse("this method is supported for " + x + " and " + y));
}
The RegEx should look for digits as the path variables, interspersed with underscores.
NB: leaving this part of the answer if Swagger works with it instead with the understanding that it could be deprecated:
#PathVariable #Parameter(description = "x_y_z - x is first ID, y is second ID, z is third ID", deprecated = true) String z
Deprecating the original method and introducing a new method with the correct parameters but different RequestMapping could also be a valid workaround.
The other part to note is that it is more common to use slashes as the delimiter rather than underscores in Spring (e.g., /aaa/bbb/x/y). You also may wish to include a validator that fits your requirements.

How to make Get Request with Request param in Postman

I have created an endpoint that accepts a string in its request param
#GetMapping(value = "/validate")
private void validateExpression(#RequestParam(value = "expression") String expression) {
System.out.println(expression);
// code to validate the input string
}
While sending the request from postman as
https://localhost:8443/validate?expression=Y07607=Curr_month:Y07606/Curr_month:Y07608
// lets say this is a valid input
console displays as
Y07607=Curr_month:Y07606/Curr_month:Y07608 Valid
But when i send
https://localhost:8443/validate?expression=Y07607=Curr_month:Y07606+Curr_month:Y07608
//which is also an valid input
console displays as
Y07607=Curr_month:Y07606 Curr_month:Y07608 Invalid
I am not understanding why "+" is not accepted as parameter.
"+" just vanishes till it reaches the api! Why?
I suggest to add this regular expression to your code to handle '+' char :
#GetMapping(value = "/validate")
private void validateExpression(#RequestParam(value = "expression:.+") String expression) {
System.out.println(expression);
// code to validate the input string
}
I didn't find any solution but the reason is because + is a special character in a URL escape for spaces. Thats why it is replacing + with a " " i.e. a space.
So apparently I have to encode it from my front-end
Its wise to encode special characters in a URL. Characters like \ or :, etc.
For + the format or value is %2. You can read more about URL encoding here. This is actually the preferred method because these special characters can sometimes cause unintended events to occur, like / or = which can mean something else in the URL.
And you need not worry about manually decoding it in the backend or server because it is automatically decoded, in most cases and frameworks. In your case, I assume you are using Spring Boot, so you don't need to worry about decoding.

How to avoid #RequestParam concatenating when a "+" operator is recieved as parameter?

I'm new to Spring Framework and I'm trying to build this simple calculator app.
I've got this #RestController with this method:
#RequestMapping(value = "/calculate", method = RequestMethod.GET)
public String calculate(#RequestParam(value = "a") String a, #RequestParam(value = "b") String b, #RequestParam(value = "operator") String operator) {
//System.out.println(operator);
if(operator.equals("+")){
return String.valueOf((Integer.valueOf(a) + Integer.valueOf(b)));
}
if(operator.equals("-")){
return String.valueOf((Integer.valueOf(a) - Integer.valueOf(b)));
}
if(operator.equals("*")){
return String.valueOf((Integer.valueOf(a) * Integer.valueOf(b)));
}
if(operator.equals("/")){
return String.valueOf((Integer.valueOf(a) / Integer.valueOf(b)));
}
Ok, the problem here is that when I send a "+" parameter to sum the two variables the program is concatenating both int's instead of performing the sum.
The rest of the operations are working fine, except for the sum. I've tried sending a "/+" without luck.
Any idea how this can be solved and most importantly, why is this happening ?
Thanks a lot :D
You should be aware of that + is a special character. To use + as a value you need to encode it as %2B in your GET request.
For example:
http://localhost:8080/calculate?a=3&b=5&operator=%2B

How to encode an authorization request_uri

I need to construct a custom request_uri for an implementation of Spring OAuth2. What specific code should be used to properly encode each of the parameters in the request_uri?
The full, unencoded, request_uri is as follows, but is resulting in an error that indicates that a token is not granted:
http://localhost:9999/uaa/oauth/authorize?client_id=acme
&redirect_uri=http://localhost:8080/login&response_type=code
&state=13ab71ae-c8ed-4370-a60f-dd7fe47ed763
As you can see, the individual parameters are:
client_id=acme
redirect_uri=http://localhost:8080/login
response_type=code
state=13ab71ae-c8ed-4370-a60f-dd7fe47ed763
And the code that was used to construct the above request_uri is:
CsrfToken csrf = (CsrfToken) attr.getRequest().getAttribute(CsrfToken.class.getName());
String attrToken = csrf.getToken();
authorizationRequest.setState(attrToken);
String newRequestUri = "http://localhost:9999/uaa/oauth/authorize?";
String clientId = authorizationRequest.getClientId();
newRequestUri = newRequestUri + "client_id=" + clientId;
String redirectUri = authorizationRequest.getRedirectUri();
newRequestUri = newRequestUri + "&redirect_uri="+redirectUri;
Set<String> respTypes = authorizationRequest.getResponseTypes();
String respType = respTypes.iterator().next();//this plucks the first one, but is not safe for when there is a list.
newRequestUri = newRequestUri +"&response_type="+respType;
String state = authorizationRequest.getState();
newRequestUri = newRequestUri + "&state="+state;
attr.setAttribute("javax.servlet.forward.request_uri", newRequestUri, RequestAttributes.SCOPE_REQUEST);
//now re-set the request attributes to reflect the changes we just made
RequestContextHolder.setRequestAttributes(attr);
More specifically, this OP asks what syntax should be used to encode the following string values in the code above: newRequestUri, clientId, redirectUri, respType, and state.
The Official OAuth2 Spec says you can use the application/x-www-form-urlencoded Content-Type and UTF-8 encoding, but then also gives this example:
/authorize?response_type=code&client_id=s6BhdRkqt3
&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
Similarly, the Spring OAuth2 Developer Guide only contains one use of the word encode.
Seems you are looking for what is commonly called percent-encoding or URL-encoding. There are functions for this in almost every language's HTTP library, either for a single value or for a set of key-value pairs.
In practice application/x-www-form-urlencoded is almost the same as URL-encoded.

DisplayTool installation and usage

I am using Velocity 1.7 to format string and I had some trouble with default values. Velocity by itself has no special syntax for case when value is not set and we want to use some another, default value.
By the means of Velocity it looks like:
#if(!${name})Default John#else${name}#end
which is unconveniant for my case.
After googling I've found DisplayTool, according to documentation it will look like:
$display.alt($name,"Default John")
So I added maven dependency but not sure how to add DisplayTool to my method and it is hard to found instructions for this.
Maybe somebody can help with advice or give useful links?..
My method:
public String testVelocity(String url) throws Exception{
Velocity.init();
VelocityContext context = getVelocityContext();//gets simple VelocityContext object
Writer out = new StringWriter();
Velocity.evaluate(context, out, "testing", url);
logger.info("got first results "+out);
return out.toString();
}
When I send
String url = "http://www.test.com?withDefault=$display.alt(\"not null\",\"exampleDefaults\")&truncate=$display.truncate(\"This is a long string.\", 10)";
String result = testVelocity(url);
I get "http://www.test.com?withDefault=$display.alt(\"not null\",\"exampleDefaults\")&truncate=$display.truncate(\"This is a long string.\", 10)" without changes, but should get
"http://www.test.com?withDefault=not null&truncate=This is...
Please tell me what I am missing. Thanks.
The construction of the URL occurs in your Java code, before you invoke Velocity, so Velocity isn't going to evaluate $display.alt(\"not null\",\"exampleDefaults\"). That syntax will be valid only in a Velocity template (which typically have .vm extensions).
In the Java code, there's no need to use the $ notation, you can just call the DisplayTool methods directly. I've not worked with DisplayTool before, but it's probably something like this:
DisplayTool display = new DisplayTool();
String withDefault = display.alt("not null","exampleDefaults");
String truncate = display.truncate("This is a long string.", 10);
String url = "http://www.test.com?"
+ withDefault=" + withDefault
+ "&truncate=" + truncate;
It might be better, though, to call your DisplayTool methods directly from the Velocity template. That's what is shown in the example usage.

Resources