I have a spring MVC application(RestFul), The controller has a method/API which returns
Map<Long, List<Long>>.
I need to call the above API in another web application. To do this I have written a client program which will internally call
the API and return the data.
But instead of sending
Map<Long, List<Long>>
it always sends data in
Map<String, List<String>>.
Can't I send directly
Map<Long, List<Long>>
If I create a BO/TO(Java Bean) and which has a property of type Map>
then I am able to get the data in proper format
Below is the code snippet.
public Map<Long, List<Long>> get(Long sourceId){
Map<Long, List<Long>> map = null;
// codes to perform operation and putting data into map.
return map;
}
Can you please suggest what is the issue ?
Anything sent over the wire is a String ... but your code picking up the response should convert it to long. Well it would if it were a spring-mvc controller with the correct method signature.
Are you using Javascript? Try to use the javascript parseFloat method on your JSON data
Related
I am currently working on rewriting the backend of a PHP Symfony application with Spring boot. I cannot touch the front-end code, and have to modify my backend to work in accordance with existing request structure.
However, I am currently stuck with something that i need some help with
some examples of an endpoint looks as below
/app/api/incidents?customSearch=Some reason&dt=month&order[timeStart]=desc
/app/api/incidents?customSearch=Some reason&dt=month&order[timeResolved]=desc
How can I map these endpoints to my RestController as a Map ? as per latest tomcat configuration square brackets are not valid and hence unsupported, throwing a 400 error. Adding a configuration for tomcat to support these characters, resolves the issue.
But how can I map the above request params into an order hashmap ?
There are other parameters that needs to be mapped as well
e.g. timeStart
/app/api/incidents?timeStart[after]=2022-10-11 00:00:00&timeStart[before]=2022-10-13 23:59:59&dt=range&order[incidentSeverity.orderBy]=desc
based on these values, the corresponding query needs to be formed.
#GetMapping
public ResponseEntity<String> test(#RequestParam(name = "order") Map<String, String> order) {
log.info("order : " + order);
return ResponseEntity.ok("Hello");
}
But order is mapped to null
Can anyone please help me with this. I have already wasted way more time than I should have.
I believe that's because of name in (#RequestParam(name = "order"). Spring boot tries to bind a request parameter named order when you put name not the whole map. You should remove nameto accept all parameters to a map.
#GetMapping
public ResponseEntity<String> test(#RequestParam Map<String, String> order) {
log.info("order : " + order);
return ResponseEntity.ok("Hello");
}
The problem should not be the code you have but the url that you send that is broken in an earlier place
/app/api/incidents?customSearch=Some reason&dt=month&order[timeStart]=desc
Try with
/app/api/incidents?customSearch=Some%20reason&dt=month&order[timeStart]=desc
you should normally encode empty spaces in query parameter values.
I'm using Feign to do REST calls on a remote Java interface.
Interface is defined as follow:
#Produces({"text/xml", "application/json"})
Since JAXRSContract simple sends data.template().header("Accept", serverProduces); server choose to answer with xml payload.
Is there any way I can force Feign to ask for `JSON payload in this situation?
You can override headers using interceptors:
Feign.builder().setInterceptor(requestTemplate -> {
Map<String, Collection<String>> map = new HashMap<>(requestTemplate.headers());
map.put("Accept", Collections.singleton("application/json"));
requestTemplate.headers(null);
requestTemplate.headers(map);
});
The method requestTemplate.headers(map) looks like poorly designed, and if you pass a valid map, its values are added to the inner header map; if you pass null instead the inner header map is reset. That's why you need to call it twice.
Is it possible to send map as parameter in a GET call.? i searched and i could find for list and set collection. But did not find anything for map collection.
I tried the following,
My controller method looks like this.
#GetMapping("/test")
public ResponseEntity<?> mapTest(#RequestParam Map<String,String> params) {
LOG.info("inside test with map "+ params );
return new ResponseEntity<String>("MAP", HttpStatus.OK);
}
And i sent the following request from postman
http://localhost:8080/test?params={a:abc,b:bcd}
Everything works without errors and exceptions. But the map which i received looks like key=params , value={a:abc,b:bcd}
I expected the received map to be like key1="a" value1=abc ,key2="b" value2="bcd"
This is documented in the Spring MVC guide:
When an #RequestParam annotation is declared as Map<String, String> or MultiValueMap<String, String> argument, the map is populated with all request parameters.
This means that the response you currently get is the expected result. The Map contains a list of all parameters, and in your case, you only have a single parameter called param.
If you need a custom parameter mapping, you'll have to implement it by yourself. Since you're not using JSON either, you probably have to manually parse the parameter.
However, if your goal is to have a dynamic map of parameters, you can still use the Map<String, String>, but you'll have to change your request into:
http://localhost:8080/test?a=abc&b=bcd
I have rest server with spring.
There is a lot of requests where one of the params is fields fields is the set of fields that server should return in response. like: /?fields=[id,name] and server should return JSON object with both fields
I would like to know what is the best practice for generating such response.
We do it like this:
private Map<String, Object> processBook(BookEntity book, Set<String> fields, String locale){
Map<String, Object> map = new HashMap<String, Object>();
//..
if(fields.contains(ID)){
map.put(ID, book.getId());
}
if(fields.contains(ISBN)){
map.put(ISBN, book.getIsbn());
}
if(fields.contains(DESCRIPTION)){
if(locale.equals(UserLocale.UK)) map.put(DESCRIPTION, book.getDescriptionUa());
else if(locale.equals(UserLocale.RU)) map.put(DESCRIPTION, book.getDescriptionRu());
else map.put(DESCRIPTION, book.getDescriptionEn());
}
//..
return map;
}
Maybe there is much better alternative?
Note that in your case you obtain all data from DB - fully filled BookEntity object, and then show only requested fields.
In my opinion it'd be "much better alternative" to delegate field list to appropriate downstream integration call and get BookEntity object only with necessary fields. Then mentioned above method will reduce to just one line, your DB responses will be more lightweight, so it will bring simplicity and optimization gain to your system.
Any adequate DB provides such functionality: SQL or NoSQL, etc.
P.S. Plus standard approach of Object to JSON mapping such as Jackson or GSON at top level.
Instead of having a Map, you could have and object with the attributes you need and set them, instead of adding to map.Then you can use Google's Gson to transform your object into a Json object.Take a look at this quick tutorial.
One approach is to have an asMap function.
Map<String, Object> map = book.asMap();
map.keySet().retainAll(fields);
I am using Spring MVC for a Rest API and am trying to call the following method on the controller:
#RequestMapping(method=RequestMethod.POST, value=IApiVersion.VERSION_ALL_PREFIX + ORG_RESPONSE_PAUSE_WRITES_URL)
public #ResponseBody Boolean setResponsePauseWrites(#RequestParam List<Long> orgIds, #RequestParam boolean pauseWrites){
validateDatabusClient();
organizationService.setPauseResponseWrites(orgIds, pauseWrites);
return Boolean.TRUE;
}
I am using Spring's RestTemplate to submit the request like this:
#Override
public void setPauseWrites(List<Long> orgIds, boolean pauseWrites){
String orgIdString = StringUtils.collectionToCommaDelimitedString(orgIds);
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("orgIds", orgIdString);
parameters.add("pauseWrites", Boolean.toString(pauseWrites));
restTemplate.postForObject(orgResponsePauseWritesURL, parameters, Boolean.class);
}
This works fine and all, but I would prefer to not need to convert the list of orgIds to a comma delimited string. I am frustrated because the spring mvc controller has no problem converting the strings back to the parameters it is expecting, List and boolean. I would expect the RestTemplate to have a built in message converter to handle basic java classes like List and Boolean.
When I try this code:
#Override
public void setPauseWrites(List<Long> orgIds, boolean pauseWrites){
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>();
parameters.add("orgIds", orgIds);
parameters.add("pauseWrites", Boolean.valueOf(pauseWrites));
restTemplate.postForObject(orgResponsePauseWritesURL, parameters, Boolean.class);
}
I get the following exception message:
org.springframework.http.converter.HttpMessageNotWritableException: Could not write request: no suitable HttpMessageConverter found for request type [java.util.ArrayList]
What is my best option? I am planning on continuing to convert the parameters to Strings for the rest template call for now, but I would like to know if there is a MessageConverter I can add to my RestTemplate to make this work. Currently I am just using the default MessageConverters. I have tried adding the MappingJacksonMessageConverter and changing my content type header to support json, but I get the same results.
Thank you.
A message converter does not just deal with the Java type (such as Boolean) that is of interest to a Java programmer. Its job is to covert Java objects to and from a byte stream representation that has a format that is identified by a media type name. Spring is only ever going to provide media converters for widely used media types. There is no widely used media type for a list of arbitrary Java objects; how could there be? As for a list of integers; I doubt there will ever be such a type, ironically, because it is just a bit too simple. The TSV (tab separated values) media type is a simple type that can represent a simple list of integers.
So, yes, you will have to write your own converter.