Spring boot request mapping - spring

I have following request parameters.
a
b
c
d
e
f
Request can contain all the parameters or some of them. I am currently using regex /** to resolve this.
Is there any way to explicitly mention the request mapping instead ** and say it is optional. And any order also should match.
/a/1/b/f2
and
/b/f2/a/1
Both should match that mapping.

There is no way to achieve this via #PathVariable's. If you want the flexibility of random order & number of path variables. You can just do the following;
#GetMapping("/myEndpoint/**")
public void theEndpoint(HttpServletRequest request) {
String requestURI = request.getRequestURI();
Stream.of(requestURI.split("myEndpoint/")[1].split("/")).forEach(System.out::println);
}
You can put a .filter(StringUtils::isNotBlank) in case /myEndpoint/a///b/c
Will give you
a
1
b
f2
d
x
when you call /myEndpoint/a/1/b/f2/d/x
b
f2
1
when you call /myEndpoint/b/f2/1
Also, be aware that you'd need some anchor base in your endpoint, e.g. /myEndpoint. Otherwise all your other endpoints will be conflicted with this endpoint.
ps. Better to use request params for such inputs tbh, not sure your requirement here, but just FYI. It is not the best to have such a hacky structure really...

You can make a RequestParam optional by adding the required flag false.
#RequestParam(value = "a", required=false)
For PathVariables i would try to use the Optional type but i have never done this before.
#PathVariable Optional<String> a for /path/{a}

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.

grape-api - Force empty string to set values to null

I am creating an API endpoint which contains a file upload field and a few string fields. My goal is to allow clients to clear values on those string fields, i.e. the DB should persist these values as null.
However, due to the fact that the request may contain files, the client should be setting the Content-type header to multipart/form-data. This implies that client cannot send a representation of "null", but can only send an empty string to indicate the intent of clearing the value for a given string field.
Is there a way for grape-api library to know that when it is receiving a multipart request it should be able to nullify blank string values in the params, or is there a better approach to what I am trying to achieve?
Grape.configure do |config|
config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
end
you can override the param builder. extend the default one and override the build_params method or monkey patch it.
params.transform_values {|v| v.eql?('') ? nil : v }

Spring request mapping wildcard exceptions

Can I put /** wildcard in a middle of request mapping such as: "/some/resource/**/somthing"
In Spring 3 I can do this
#RequestMapping("/some/resource/**")
to map
/some/resource/A -> ControllerMethod1
/some/resource/A/B -> ControllerMethod1
/some/resource/A/B/C/D/E/F -> ControllerMethod1
for any number of paths parts
However this mapping is too greedy and will not allow me to map a sub URL #RequestMapping("/some/resource/**/somthing") to another controller such as
/some/resource/A/somthing -> ControllerMethod2
/some/resource/A/B/somthing -> ControllerMethod2
/some/resource/A/B/C/D/E/F/somthing -> ControllerMethod2
How can i do this?
I thinks it's not possible to use that ant style in url mapping as you require, because it will stop on the next path separator character '/'.
I would suggest you to try 16.3.2.2. URI Template Patterns with Regular Expressions in order to map just the last part of the request (haven't tried this approach yet).
Also you can match the rest of the request using PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, and apply some expression there. Check this post.
Otherwise you should use request parameters to match that condition 16.3.2.6. Request Parameters and Header Values.
You can narrow request matching through request parameter conditions such as "myParam", "!myParam", or "myParam=myValue". The first two test for request parameter presense/absence and the third for a specific parameter value. Here is an example with a request parameter value condition.
In this case you will map something like that using params
#RequestMapping(value = {"/some/resource/**"}, params="somthing")
or use the annotation request parameter with not required attribute in method signature:
public void test(#RequestParam(value = "somthing", required=false) String str) {

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