Handling dynamic query parameter in CXF restful webservice - java

I want to handle dynamic query parameter in CXF RESTful service.
My concern here is from the service end i will not be knowing the number of parameters/key names that comes with the request. Could you please let me know how we can handle this situation/scenario.
For example my client code will look like below ,
Map<String, String> params = new HashMap<>();
params.put("foo", "hello");
params.put("bar", "world");
WebClient webClient = WebClient.create("http://url");
for (Entry<String, String> entry : params.entrySet()) {
webClient.query(entry.getKey(), entry.getValue());
}
Response res = webClient.get();
The below code works fine for me,
public String getDynamicParamter(#Context UriInfo ui){
System.out.println(ui.getQueryParameters());
MultivaluedMap<String, String> map = ui.getQueryParameters();
Set keys=map.keySet();
Iterator<String > it = keys.iterator();
while(it.hasNext()){
String key= it.next();
System.out.println("Key --->"+key+" Value"+map.get(key).toString());
}
return "";
}
However , could you please let me know the below,
Whether it is standard way?
And, is there any other ways to achieve it?
CXF version : 3.1.4

Getting the query parameters from the UriInfo
As you already guessed, there are some methods in UriInfo API that will provide you the query parameters:
getQueryParameters(): Returns an unmodifiable map containing the names and values of query parameters of the current request. All sequences of escaped octets in parameter names and values are decoded, equivalent to getQueryParameters(true).
getQueryParameters(boolean): Returns an unmodifiable map containing the names and values of query parameters of the current request.
The UriInfo can be injected in a resource class member using the #Context annotation:
#Path("/foo")
public class SomeResource {
#Context
private UriInfo uriInfo;
#GET
public Response someMethod() {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
...
}
}
And can be injected in a method parameter:
#GET
public Response someMethod(#Context UriInfo uriInfo) {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
...
}
To get the unparsed query string, do the following:
#GET
public Response someMethod(#Context UriInfo uriInfo) {
String query = uriInfo.getRequestUri().getQuery();
...
}
Getting the query parameters from the HttpServletRequest
You could achieve a similiar result by injecting the HttpServletRequest with the #Context annotation, just like the UriInfo mentioned above. Here are a few methods that can be useful:
getParameterMap(): Returns an immutable map containing the names and values of parameters sent in the request.
getParameterNames(): Returns an Enumeration containing the names of the parameters contained in the request.
getParameterValues(String): Returns an array containing all of the values the given request parameter.
getParameter(String): Returns the value of a request parameter. Should only be used when you are sure the parameter has only one value. Otherwise, only the first value for the given parameter name will be returned.
getQueryString(): Returns the query string that is contained in the request URL.

Related

Is there a way that I can limit the number of request parameters in my method header in SpringBoot?

If I have a method that has like 10 request parameters and I may not always need all of them
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#RequestParam String optionalRequestParam1,
#RequestParam String optionalRequestParam2,
...
#RequestParam String optionalRequestParam10)
So in this header I'd like something that is like
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#RequestParam RequestParamBuilder requestParamBuilder)
and then it would just build an object for me with all valid parameters sent through filled out and the rest being null or something
You can have multiple parameters without defining their names by just using a Map:
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#RequestParam Map<String, Object> params) {
log.info("Params: {}", params.entrySet();
}
How to make the call:
curl --location --request GET 'http://localhost:8080/whatever?integer=45&string="some text"&boolean=true'
Output:
Params: [integer=45, string="some text", boolean=true]
If you wanted the parameters to be passed into an object, you can use a POJO as you were but remove the #RequestParam notation:
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(RequestParamBuilder requestParamBuilder)
Then create a class for RequestParamBuilder. You can mark the fields inside the POJO as #NotNull etc to handle validation and build out the object with only the params that were included in the request, but the POJO must be annotated with #Valid if you wish for spring to validate it this way:
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#Valid RequestParamBuilder requestParamBuilder)

RestEasy rest client format query parameters

I need to call a 3rd party rest service from my Java application with a formatted URI:
.../rest/v1/search?filter[first_name]=john&filter[last_name]=smith
The reason for this format is, that there are several query fields (20+) and I can't create a #QueryParam parameter for every fieldname.
#POST
#Path("/rest/v1/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
Response searchCustomer(#QueryParam("filter") Map<String, String> filter);
My example with a map results in
/rest/v1/search?filter={first_name=John,+last_name=Smith}
How do I achieve the URI form with square brackets?
You can use #Context UriInfo to get a map of all the query params instead of hardcoding every param
example:
#POST
#Path("/rest/v1/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response searchCustomer(#Context UriInfo info){
//here are some different ways you can use the uriinfo
MultivaluedMap<String,String> map = info.getQueryParameters();
String firstName = map.getFirst("filter");
String lastName = map.getFirst("lastName");
}

How to validate redundant query parameters with RestEasy?

I have service(WildFly 10.1) which looks like that :
#GET
#Path("/retrieve")
public Response getModels(#BeanParam ModelQueryParams queryParams) {
return getModels();
}
With ModelQueryParams:
public class ModelQueryParams{
#QueryParam("offset")
private Long offset;
#QueryParam("limit")
private Long limit;
}
So the user can call endpoint like:
/retrieve?offset=100&limit=4
But how can I validate case when user pass into the query wrong parameter?
/retrieve?offset=100&limit=4&WRONG_PARAMETER=55
Is there the way to validate it somehow?
if you don't have any field or method parameters annotated with #QueryParam, then those extra parameters are not your problem and it's best to deal with only parameters you expect for your resource.
If you still need access to all query parameters, then inject UriInfo with #Context and call it's getQueryParameters() to get a MultivaluedMap of request parameters

How to pass List or String array to getForObject with Spring RestTemplate

I am developing some restful services with Spring. I have trouble with passing/getting string array or large string as parameters to my service controller. My code examples are like below;
Controller:
#RequestMapping(value="/getLocationInformations/{pointList}", method=RequestMethod.GET)
#ResponseBody
public LocationInfoObject getLocationInformations(#PathVariable("pointList") String pointList)
{
// code block
}
Sample point list:
String pointList = "37.0433;35.2663,37.0431;35.2663,37.0429;35.2664,37.0428;35.2664,37.0426;35.2665,37.0424;35.2667,37.0422;35.2669,37.042;35.2671,37.0419;35.2673,37.0417;35.2674,37.0415;35.2674,37.0412;35.2672,37.0408;35.267,37.04;35.2667,37.0396;35.2665,37.0391;35.2663,37.0388;35.2662,37.0384;35.266,37.0381;35.2659,37.0379;35.2658,37.0377;35.2657,37.0404;35.2668,37.0377;35.2656,37.0378;35.2652,37.0378;35.2652,37.0381;35.2646,37.0382;35.264,37.0381;35.2635,37.038;35.263,37.0379;35.2627,37.0378;35.2626,37.0376;35.2626,37.0372;35.2627,37.0367;35.2628,37.0363;35.2628,37.036;35.2629,37.0357;35.2629,37.0356;35.2628,37.0356;35.2628,37.0355;35.2626";
Web service client code:
Map<String, String> vars = new HashMap<String, String>();
vars.put("pointList", pointList);
String apiUrl = "http://api.website.com/service/getLocationInformations/{pointList}";
RestTemplate restTemplate = new RestTemplate();
LocationInfoObject result = restTemplate.getForObject(apiUrl, LocationInfoObject.class, vars);
When I run client side application, it throws a HttpClientErrorException: 400 Bad Request, I think long location information string causes to this problem. So, how can I solve this issue? Or is it possible posting long string value as parameter to web service?
Thx all
List or other type of objects can post with RestTemplate's postForObject method. My solution is like below:
controller:
#RequestMapping(value="/getLocationInformations", method=RequestMethod.POST)
#ResponseBody
public LocationInfoObject getLocationInformations(#RequestBody RequestObject requestObject)
{
// code block
}
Create a request object for posting to service:
public class RequestObject implements Serializable
{
public List<Point> pointList = null;
}
public class Point
{
public Float latitude = null;
public Float longitude = null;
}
Create a response object to get values from service:
public class ResponseObject implements Serializable
{
public Boolean success = false;
public Integer statusCode = null;
public String status = null;
public LocationInfoObject locationInfo = null;
}
Post point list with request object and get response object from service:
String apiUrl = "http://api.website.com/service/getLocationInformations";
RequestObject requestObject = new RequestObject();
// create pointList and add to requestObject
requestObject.setPointList(pointList);
RestTemplate restTemplate = new RestTemplate();
ResponseObject response = restTemplate.postForObject(apiUrl, requestObject, ResponseObject.class);
// response.getSuccess(), response.getStatusCode(), response.getStatus(), response.getLocationInfo() can be used
The question is related to GET resource, not POST. Because of that I think that "accepted answer" is not the correct one.
So for other googlers like me that finds this, Ill add what helps me:
GET resources can receive a string list via #PathVariable or #RequestParam and even correctly bind it to a List<String> if you do pass the list separated by ,.
Your API can be:
#RequestMapping(value="/getLocationInformations/{pointList}", method=RequestMethod.GET)
#ResponseBody
public LocationInfoObject getLocationInformations(#PathVariable("pointList") List<String> pointList) {
// code block
}
And your call would be:
List<String> listOfPoints = ...;
String points = String.join(",", listOfPoints);
String apiUrl = "http://api.website.com/service/getLocationInformations/{pointList}";
LocationInfoObject result = restTemplate.getForObject(apiUrl, LocationInfoObject.class, points);
Note that you must send lists to API using , as separator, otherwise the API cannot recognize it as a list. Also you cannot just add your list directly as a parameter, because depending on how it's mashalled the generated string may not be compatible.
Firstly you've passed a map as parameters but your controller expects these as a path variable. All you need to do is make the 'pointlist' value part of the URL (without the curly bracket placeholders). e.g.:-
http://api.website.com/service/getLocationInformations/pointList
Next you need to ensure you have message converters set up so that your LocationInfoObject is marshalled into an appropriate representation (suggest JSON) and unmarshalled the same way.
For the Rest template:
restTemplate.setMessageConverters(...Google MappingJackson2HttpMessageConverter...);
For the server you just need to add Jackson to the classpath (if you want multiple representations you'd need to configure each one manually - Google will be your friend here aswell.

How is this JAX-RS webservice working?

I'm going over few pieces of code and came across something like this
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces({ MediaType.APPLICATION_JSON , MediaType.APPLICATION_XML })
public Response getMedia(
#HeaderParam("X-META") String metaToken,
#QueryParam("provider") String provider, MultivaluedMap<String, String> formContext ,
#Context UriInfo uriInfo) {
Map<String, String> context = APIUtils.buildContext(formContext);
return getMediaInternal(metaToken, provider, context, uriInfo);
}
I know that Annotated vars are injected by jersey but I'm clueless as how the formContext is being injected. It's not annotated. What all values are put in here by jersey ? All the post parameters ? Whats a general rule to deduce whats being populated when not annotated ? Any pointers to reference material or a brief description of whats happening here is helpful
According to the Jersey User Guide, it seems like jersey will inject the MultiValuedMap<> type on a #POST request because form parameters are part of the message entity. This is the example:
Example 3.13. Obtaining general map of form parameters
#POST
#Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
// Store the message
}

Categories