Can a query parameter name be variable? Like: #QueryParam("//anything") - java

I have a resource class and within it a #GET that takes one query param called operation (this should be static) and then I want to take a variable number of other query params that can be named anything.
My first thought was to do something like this:
public Response get(
#QueryParam("operation") String operation,
#QueryParam("list") final List<String> list) {
//do stuff
}
The problem here is that I would have to make a request like:
...?operation=logging&list=ABC&list=XYZ
While what I want is to be able to have something like this:
...?operation=logging&anything=ABC&something_else=XYZ
Is there a way to make the list query param #QueryParam(//anything)?
In doing some information gathering I ran across this sort of approach:
#GET
public void gettest(#Context UriInfo ui) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
String operation = queryParams.getFirst("operation");
for (String theKey : queryParams.keySet()) {
System.out.println(queryParams.getFirst(theKey));
//do stuff with each other query param
}
}
Is multivaluedmap the way to go for this situation -- Or is there a way to use a variable query param name? Or a better approach? Thanks.
Edit/Update:
This is using javax.ws.rs
The use case is: this application being used as a tool for mocking responses (used for testing purposes in other applications). The mock responses are retrieved from a DB by looking up the 'operation' and then some sort of 'id'. The actual id used could be any of the "list" query params given. The reason for this is to give flexibility in different applications to use this mock service -- the urls in applications may be constructed many different ways and this makes if so one doesn't have to change around their code to be able to use the mock service.

As in this question, use a map:
#RequestMapping(value = {"/search/", "/search"}, method = RequestMethod.GET)
public String search(
#RequestParam Map<String,String> allRequestParams, ModelMap model) {
return "viewName";
}

Related

Store REST query parameters as static fields

Is it a good practice to store the query parameters as static fields to handle a request? For instance, my endpoint accepts two query parameters. To validate and process the request body, I have to pass these query parameters to all the low-level methods (like daisy-chaining). To avoid this, I can store these parameters as static fields within a static class and access these parameters whenever required instead of passing it around. I am fairly new to REST development and I don't want to reinvent the wheel if there are existing patterns to handles the problem I facing. Any thoughts on my approach?
In the code below, I am passing query1, query2, query3 parameters to validateRequest API, which is again passing some of the query parameters to its internal methods. This daisy-chaining will continue to low-level methods. My question is if store these query parameters in a static class, I don't have to pass the query parameters around. When an API requires query parameters, it can call the static class to access the parameters.
Pseudocode:
public class Temp {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response post(
#QueryParam("query1") final String query1,
#QueryParam("query2") final String query2,
#QueryParam("query3") final String query3,
final Object requestBody) {
validateRequest(requestBody,query1,query2,query3)
return Response.status(Status.OK).build();
}
private void validateRequest(Object requestBody, String query1, String query2, String query3) {
validateFirstPartOfRequest(requestBody,query1);
validateSecondPartOfRequest(requestBody,query1,query2);
validateThirdPartOfRequest(requestBody,query1),query3;
}
}
To validate and process the request body, I have to pass these query parameters to all the low-level methods I'm not affraid by this. For instance request parameters will be used for filtering a collection ressource so it sounds logic to pass these parameters until the data access layer for applying your filters at database-level (criteria query).
If you have lot of parameters just wrap them into a parameter object (https://refactoring.guru/introduce-parameter-object)
Regarding static it is really a bad idea. Static data will be shared by all requests(threads) coming to your controller and so you'll face some overlap between values.
It's not recommended to change a static variable from multiple threads just to avoid carrying it in the class instance, but if that's what you want to do, maybe you should have a look at ThreadLocal.

Having alias param names to accept Url Encoded Form data values

In my Spring web application, I have an API that accepts requests with application/x-www-form-urlencoded content type.
#RequestMapping(value = "/do-it", method = {RequestMethod.POST})
public String test(#ModelAttribute("request")RequestDTO request,HttpServletRequest
httpServletRequest, Map<String, Object> model, RedirectAttributes redirectAttributes){
.....
}
My RequestDTO has following fields in it.
public class RequestDTO {
private String paramOne;
private String paramTwo;
// standard getters and setters
}
This implementation works fine, all the request params get mapped to the request dto as expected. However, now I have this requirement to accept the requests with the fields in following pattern.
param_one, param_two
I understand that, using #JsonProperty annotation on the fields in my request dto is not gonna work in my case since the request is not in the type of application/json.
The only way I have found to solve the issue is creating new setters like following (which looks ugly in my opinion when it comes to naming convention).
public void setParam_one(String param_one) {
this.paramOne = param_one;
}
Can some one help me to find a better way to get this done? I cannot change the param names in original request dto.
Thank you..!
I was able to get this done. Thanks to #neetash for guiding me.
Everything I needed to have was a Custom HandlerMethodArgumentResolver to map the post request body data to the object that I wanted to get.
I followed following linked tutorial to implement it. It contains every single thing someone needs to know to create a HandlerMethodArgumentResolver.
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/

POST or GET what should I use in case request parameters exceeds 15? [duplicate]

This question already has answers here:
Design RESTful query API with a long list of query parameters [closed]
(4 answers)
Closed 3 years ago.
I am designing a rest API, to get a resource based on some parameters but in some cases these parameters are between 15-20 in number.
I am thinking of using a POST request to get the resource based on these 15 parameters. I know that POST request should not be used in case of getting the resource.
I want to know if there is a better option to handle this then sending POST request?
You can use Get service by using Map. It will accept all param.
/test?A=ABC&B=123
#RequestMapping(value="/test",method = RequestMethod.GET)
public String testUrl(#RequestParam Map<String, String> parameters)
{
println(parameters.get("A"));
println(parameters.get("B"));
return parameters.get("A");
}
Out Put Will Be
ABC
123
GET doesn't restrict the number of parameters
the only restriction is the length of the URL (which contains these parameters)
So if you're expecting that the parameters and values would cause a long URL, you can use POST instead
Standard says that URL length should be not more than 2,083 characters
even if some browsers/servers allow more, it's better to stick on this value for a wide-range support for all browsers/servers/gateways
In order to make your #Controller code more concise (e.g. get rid of 15x #RequestParam) you can use #ModelAttribute annotation.
#GetMapping(value="/things")
public List<Thing> findAllThings(#ModelAttribute ThingDTO thing) {
// logic
}
and your ThingDTO class like that:
public class ThingDTO {
private String name;
private ThingType type;
[...] //getters, setters etc.
}
This is going to map your class attributes to the #RequestParams. The code looks a bit cleaner imho.
When it comes to the URL length you should check the topic here: What is the maximum length of a URL in different browsers? and decide if there's possibility of exceeding the limit.
What should you use? For data retrieval I'd in 99% cases go with GET so if the above is not a blocker for you, go with GET. If there's a chance of exceeding the limit, go with POST.
The parameter length shouldn't be a problem to handle for the server.
You should read about how to design a rest api. I would recommend you to build it like this:
/api/{version}/{entity}/{id}
If you are using Spring Boot this is very simple to build.
Just write a Controller like this
#RestController
#RequestMapping("/api/users")
public class UsersService {
#Autowired
UsersRepository userRepo;
#RequestMapping(value="/find-all", method=RequestMethod.GET)
public Page<User> getAllUsers(#RequestParam(value="page", defaultValue = "0", required=false)Integer page,
#RequestParam(value="size", defaultValue= "20", required=false)Integer size){
...
}
#RequestMapping(value="/find/{id}")
public ResponseEntity<User> getUserById(#PathVariable(name="id")Long id){
...
}
#RequestMapping(value="/save", method=RequestMethod.POST)
public ResponseEntity<User> createUser(#RequestBody User user){
...
}
#RequestMapping(value="/delete/{id}", method = RequestMethod.GET)
public ResponseEntity<Void> deleteUser(#PathVariable(name="id")Long id){
...
}
}
Hope this sample code helps you to build your api. Just pass your ID as a PathVariable like shown in deleteUser() or findUser().
If you need more Parameters, you can extend the list or use RequestParam like used in getAllUsers().
The parameters are optional but need a default value.

Spring, middle score in request parameter

Is there a way to map a query parameter with a middle score using requests in spring?
I have no problem binding single worded parameters doing this:
Uri example: http://localhost:8080/test/?product=hotels
public class CitiesRequest{
private ProductType product;
public ProductType getProduct() {
return this.product;
}
public void setProduct(String product) {
this.product = product;
}
}
But I'd like to be able to receive parameters like this:
http://localhost:8080/test/?product-type=hotels
As Misha stated it is syntactically incorrect to have a variable name with a hyphen in Java. But Spring is fine with that and allows you to specify a parameter name (in the request) different from the variable name (in java code). For exemple, when using RequestMapping driven controller, one can write :
#RequestMapping("/test")
public ModelAndView getProduct(
#RequestParam("product-type") String productType) {
...
}
That way, getProduct will be called for a url like http://localhost/test?product-type=hotels and the parameter productTypewill receive the value hotels. And all is still purely declarative.
By default, Spring maps the query parameter key to the name of the Java variable. However, it's syntactically incorrect to have a variable name with a hyphen in Java, which explains why you're finding it particularly difficult to get Spring to set the parameter's value for you.
One workaround that might work is to just have a Map<String, String[]> parameter to represent all of the parameters. Then Spring doesn't have to map any query parameters to variable names, so the hyphenated name might end up in that map of all parameters. It may not be as comfortable as pre-split parameter objects, but it might get the hyphenated keys.
Another solution might be to configure the WebDataBinder, which controls how data from HTTP requests are mapped onto your controller's request parameters. But that's a whole can of worms, especially if you're just starting out with Spring. You can read more about it in the documentation under "data binding".

Can I use JSON data in RequestMapping in Spring MVC

I'm thinking the answer here is probably no, but just in case.
I'm doing something like this:
#RequestMapping(value="data.json", params="query=overview")
public String getOverview(#RequestBody MyRequest myRequest) {
[...]
return "overview";
}
#RequestMapping(value="data.json", params="query=detail")
public String getDetail(#RequestBody MyRequest myRequest) {
[...]
return "detail";
}
and the client is POSTing JSON data, which is deserialized by Jackson on the way in and bound to the MyRequest parameter, all working nicely.
However, I don't like the query type having to be specified in the URL of the requests. What I would like is to include the query parameter in the JSON object and use that to drive the #RequestMapping. Is this possible?
If not, I guess I will implement a single mapping and have that method delegate to others based on the incoming data, but that feels like a step in the wrong direction.
What you are trying to do does not work out of the box.
If you don't like the param why don't you add the qualifier to the URL like so:
#RequestMapping("/overview/data.json")
#RequestMapping("/detail/data.json")
If you absolutely need the functionality you mention, you could implement a custom RequestMappingHandlerMapping that would do what you want by extending that class as is done here.
It's not possible if you remove the params. You have to have something distinct between the two mappings. If you are intent on getting rid of the params, best you could do is have a single method/mapping and call your services or whatever other logic you have according to what the value of query is in your MyRequest object.
#RequestMapping(value="data.json")
public String getOverviewOrDetail(#RequestBody MyRequest myRequest) {
if (myRequest.getQuery().equalsIgnoreCase("overview")) {
[...]
return "overview"
} else if(myRequest.getQuery().equalsIgnoreCase("detail")) {
[...]
return "detail"
}
}
Since both methods are unmarshalling to the same object, you don't really need two separate methods/mappings.

Categories