Dynamically creatre response structure - java

I want to dynamically create a specific response in my controller.
#GetMapping("/test")
public ResponseEntity<?> getLanguageList() {
return ResponseEntity.ok(new Object() ??
);
}
And response on GET /test should be like that:
{
status: "OK",
info: "Hello"
}
How to do that? I don't want to create a new class for response.

Return a Map<String, Object> or Map<String, String> in the ResponseEntity. You can construct and add properties as you need in runtime to a map and it will be converted to a json structure by `ResponseEntity.ok. So, just do:
return new ResponseEntity<Map<String, Object>>(map, HttpStatus.OK) ;

Related

I have a Map<String, Object> which contains all the headers, but in response I am not able to feed it all

Map<String, Object> headerMap = JSON_MAPPER.convertValue(reqData.getHeaders(), new TypeReference<Map<String, Object>>() {});
I have a headerMap which contains headers in a key-value pair.
I need to return these headers in a response builder statement of my controller.
Header accepts (String name, Object value) as parameters
``` return Response
.status(reqData.getStatuscode())
.entity(reqData)
.header()
.type(MediaType.APPLICATION_JSON_TYPE)
.build(); ```
Please help me out!
You should get a reference to Response.ResponseBuilder and then iterate the map of the headers to populate the header values.
Possible solution below (not tested):
public Response foo(RequestData reqData) {
Response.ResponseBuilder builder = Response
.status(reqData.getStatuscode())
.entity(reqData)
.type(MediaType.APPLICATION_JSON_TYPE);
headerMap.forEach(builder::header);
return builder.build();
}

Spring boot rest endpoint returning null with fetch request

I have already created Rest Endpoint in Java spring boot. It returns appropriate response when I request it via Postman. But when I use react fetch it does not show any response in browser if return is Json.
Spring boot controller:
#RestController
#RequestMapping(path = "/v1/test")
#AllArgsConstructor(onConstructor_ = {#Autowired})
public class TestController {
...
}
Below endpoint is returning appropriate response.
#GetMapping(value = "/helloWorld", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public String getHelloWorld() {
return "Hello, World1!";
}
But when I try to hit below endpoint it returns null when I make fetch request. But it returns appropriate response when I hit it via postman.
#GetMapping(value = "/testEndpoint", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public String returnTestResponse() {
HashMap<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("results", "value2");
return "{\"a\":1, \"b\":\"foo\"}";
}
Also tried returning POJO object. But still no response.
#GetMapping(value = "/testModel", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public SearchResultsModel testModel() {
this.myService.getSearchResult();
}
React fetch call:
await fetch(ALL_ARTICLES_ENDPOINT, {
mode: 'no-cors',
method: 'GET',
redirect: 'follow',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
}).then(response => {
console.log(response);
})
.then(data => {
console.log('Success:', data);
}).catch((error) => {
console.error('Error:', error);
});
Postman have couple hidden headers which are being sent with all requests.
Check Hide auto-generated headers
What you are missing in react call is is Accept header with application/json value
EDIT:
Just saw that you are returning string as json. You need to wrap it in POJO object and return it in returnTestResponse class
SECOND EDIT:
This will work. Try to implement your POJO
#GetMapping(value = "/testEndpoint", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public YourObject returnTestResponse() {
HashMap<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("results", "value2");
return new YourObject(map);
}
Issue was caused by adding mode: 'no-cors' option in fetch request. This option helped me to get rid of cors error but it means that in return I won't be able to see body and headers in chrome.
To resolve the issue I removed mode: 'no-cors' and added #CrossOrigin annotation on my spring boot controller.

Display HashMap<String, Object> in Angular6 received from Rest API

I'm trying to display the content of HashMap received from my controller, however I get an empty map.
this is my controller:
#PostMapping(value = "getAllUsers")
public HashMap<String, Object> getAllUsers(#RequestBody String s)
{
List<User> list = userRepository.getAll(s);
HashMap<String, Object> userList = new HashMap<String, Object>();
userList.put("list", list);
userList.put("total", list.size());
return userList;
}
and this is my angular code:
getUsers() {
this.userService.getUsers("canada").subscribe((data) => {
console.log(data); // this returns an empty object
data.forEach((value: string, key: string) => { // returns an error which is forEach is not a function
console.log(key, value);
});
this.userList = data; // empty
console.log(this.userList);
});
}
this is my api service:
let URL = "api/v1";
getUsers(country: String): Observable<any> {
return this.http.post(URL + "getAllUsers", country);
}
I want to know how to manipulate and iterate the HashMap received from my controller. Thank you in advance.
Typescript is assuming the response as an Object of type any instead of a Map, so Try this:
Object.entries(data).forEach((value: string, key: string) => {
console.log(key, value);
});
The point here is not HashMap iteration but your are getting empty response data even here.
this.userService.getUsers("canada").subscribe((data) => {
console.log(data); // this returns an empty object
Add #ResponseBody
#PostMapping(value = "getAllUsers")
#ResponseBody
public HashMap<String, Object> getAllUsers(#RequestBody String s)
Now coming to iteration once you get the response which is not empty there are multiple ways to iterate map.
Object.keys(data).forEach(key => {
console.log(key, data[key]);
});
=> Better to use Http convention of using get to fetch data over post. Although data will still be received in Post but prefer get().
You are performing a POST request but in your use case you should use the GET http method in order to retrieve a resource. POST http method is used to add a resource. You have to change your controller to use #GetMapping()
#GetMapping("/getAllUsers")
public HashMap<String, Object> getAllUsers(#PathVariable String s)
{
List<User> list = userRepository.getAll(s);
HashMap<String, Object> userList = new HashMap<String, Object>();
userList.put("list", list);
userList.put("total", list.size());
return userList;
}
You should also change your parameter to that controller from #RequestBody to #PathVariable
#PathVariable annotation is used to receive bind the URI variable to the method
parameter.
#RequestBody annotation binds the content sent in (POST / PUT)
request body with the annotated variable. (Generally, you can only use
#RequestBody for the requests which can have 'body' content e.g. POST
or PUT.)
You can read about HTTP Methods here.

resttemplate getForObject map responsetype

Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I'm invoking a rest service that returns JSON like this:
{
"some.key" : "some value",
"another.key" : "another value"
}
I would like to think that I can invoke this service with a java.util.Map as the response type but that's not working for me. I get this exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map]
Should I just specify String as the response type and convert the JSON to a Map?
Edit I
Here's my restTemplate call:
private Map<String, String> getBuildInfo(String buildUrl) {
return restTemplate.getForObject(buildUrl, Map.class);
}
Here's how I'm setting up the restTemplate:
#PostConstruct
public void initialize() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
requestWrapper.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
return execution.execute(requestWrapper, body);
}
});
restTemplate.setInterceptors(interceptors);
}
Edit II
Full error message:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:108) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:549) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at idexx.ordering.services.AwsServersServiceImpl.getBuildInfo(AwsServersServiceImpl.java:96) ~[classes/:na]
RestTemplate has a method named exchange that takes an instance of ParameterizedTypeReference as parameter.
To make a GET request that returns a java.util.Map, just create an instance of an anonym class that inherits from ParameterizedTypeReference.
ParameterizedTypeReference<Map<String, String>> responseType =
new ParameterizedTypeReference<>() {};
You can then invoke the exchange method:
RequestEntity<Void> request = RequestEntity.get("http://example.com/foo")
.accept(MediaType.APPLICATION_JSON).build();
Map<String, String> jsonDictionary = restTemplate.exchange(request, responseType).getBody();
As I had previously noted, your error message is showing us that you are receiving application/octet-stream as a Content-Type.
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
As such, Jackson's MappingJackson2HttpMessageConverter cannot parse the content (it's expecting application/json).
Original answer:
Assuming your HTTP response's Content-Type is application/json and you have have Jackson 1 or 2 on the classpath, a RestTemplate can deserialize JSON like you have into a java.util.Map just fine.
With the error you are getting, which you haven't shown in full, either you've registered custom HttpMessageConverter objects which overwrite the defaults ones, or you don't have Jackson on your classpath and the MappingJackson2HttpMessageConverter isn't registered (which would do the deserialization) or you aren't receiving application/json.
I think you can achieve what you're aiming for simply using the RestTemplate and specifying a JsonNode as the response type.
ResponseEntity<JsonNode> response =
restTemplate.exchange(url, HttpMethod.GET, entity, JsonNode.class);
JsonNode map = response.getBody();
String someValue = map.get("someValue").asText();
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I ended up getting the contents as a String and then converting them to a Map like this:
String json = restTemplate.getForObject(buildUrl, String.class);
Map<String,String> map = new HashMap<String,String>();
ObjectMapper mapper = new ObjectMapper();
try {
//convert JSON string to Map
map = mapper.readValue(json, new TypeReference<HashMap<String,String>>(){});
} catch (Exception e) {
logger.info("Exception converting {} to map", json, e);
}
return map;
I know its old, but just for other people that may visit this topic:
If you want to register some additional converters with RestTemplateBuilder you also have to explicitly register default ones
#Bean
public RestTemplateBuilder builder() {
return new RestTemplateBuilder()
.defaultMessageConverters()
.additionalMessageConverters(halMessageConverter());
}
private HttpMessageConverter halMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jackson2HalModule());
TypeConstrainedMappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
halConverter.setSupportedMediaTypes(Collections.singletonList(MediaTypes.HAL_JSON));
halConverter.setObjectMapper(objectMapper);
return halConverter;
}
This worked 100% for me
in client
Map<String, Object> mapRespuesta = new HashMap<>();
mapRespuesta.put("mensaje", "Process completed successfully");
mapRespuesta.put("idResponse", id);
return new ResponseEntity<Map<String, Object>>(mapRespuesta, HttpStatus.OK);
in which it makes the connection
ResponseEntity<Map> result = restTemplate.postForEntity(url, params, Map.class);
String id = result.getBody().get("idResponse").toString();
#GetMapping(value = "getSunny/{userId}")
public Map<String, SunnyVO> getSunny(#PathVariable int sunnyId) {
Map<String, SunnyVO> newObj = new HashMap<String, SunnyVO>();
final String url = "http://localhost:8085/Sunny/getSunny/{sunnyId}";
RestTemplate restTemplate = new RestTemplate();
newObj = restTemplate.getForObject(url, Map.class, sunnyId);
return newObj;
}
It is working for me ...

Redirect to another controller after ajax call

I want to redirect to another controller after a successful ajax call also i want to send some data that I get in the response
User Controller
#RequestMapping(method = RequestMethod.POST, value = "/user/preferences")
public #ResponseBody Map<String, List<String>> userPreferences(HttpServletRequest request) {
Map<String , List<String>> finalPlaces = new LinkedHashMap<String, List<String>>();
finalPlaces.put(entry.getKey(), new ArrayList<String>(topPlaces));
return finalPlaces;
}
Ajax Call
$(".savebutton").click(function(){
$.ajax({
url: "<c:url value='/user/preferences' />",
type: 'POST',
data:{
preferences : preferences
},
success: function(response)
{
},
error: function(xhr, ajaxOptions, thrownError) {
alert('Error');
}
});
});
when the above ajax call returns successfully I want to call a method of another controller
Places Controller
#RequestMapping(method = RequestMethod.GET, value = "/places")
public ModelAndView showRecommnededPlaces(Map<String, List<String>> recommndedPlaces) {
System.out.print(recommndedPlaces);
ModelAndView model = new ModelAndView("places");
return model;
}
is it possible to directly call the places controller action from the user controller?
Thanks
Yes, you can return "redirect:/places"; in your user controller, like:
#RequestMapping(method = RequestMethod.POST, value = "/user/preferences")
public #ResponseBody String userPreferences(HttpServletRequest request) {
Map< String, Integer> userPreferences = new HashMap< String, Integer>();
.
.
.
return "redirect:/places";
}
If you are okay about putting dependency from one controller to the next, you can actually do this:
public class UserController {
#Autowired PreferencesController prefController;
#RequestMapping(method = RequestMethod.POST, value = "/user/preferences")
public #ResponseBody Map<String, List<String>> userPreferences(HttpServletRequest request) {
Map<String , List<String>> finalPlaces = new LinkedHashMap<String, List<String>>();
finalPlaces.put(entry.getKey(), new ArrayList<String>(topPlaces));
return prefController. showRecommendedPlaces(finalPlaces);
}
}
Not saying this is good - since you will have to match the response type of your called controller appropriately, but nevertheless an approach

Categories