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

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.

Related

Add query params after building the URIComponents

I am using a UriComponentsBuilder to create my GET request endpoint and it works fine.
But I am trying to make it reusable such that I can pass in as many request params and
path variables as I want without limiting it. This is fine for path variables since I
can pass as many params as I want as a map inside buildAndExpand.
But how could I do it for request param too? Please advice.
This is what I am currently doing but this is not reusable.
public Map get(String a, String b, String c, String d, String e) {
String url = "domain.com/get/{a}/{b}";
String endPoint = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("c" ,c)
.queryParam("d" ,d)
.queryParam("e" ,e)
.buildAndExpand(
new HashMap<String, String>() {{
put("a", a);
put("b", b);
}}
).toUriString();
return restTemplate.getForEntity(endPoint, Map.class);
}
I want to instead pull out the endpoint creation as a separate method as follows.
private UriComponents getUriComponent(String url, Map<String, String> params) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
return builder.buildAndExpand(params);
}
This would work fine for the path params a and b.
But how can I take in an argument here for b,c,e too which are query params.
Or some other suggestions to keep it dynamic. So I can pass in 5 queryParam for one request
and 10 queryParam for another without having to rewrite the UriComponentsBuilder each time.
Please note that preferably, I do not want to modify the url to like the following for this.
String url = "domain.com/get/{a}/{b}?c={c}&d={d}&e={e}";
So, using UriComponentsBuilder you can add new parameters to the URL.
String old = UriComponentsBuilder.newInstance()
.scheme("http")
.host("localhost")
.queryParam("a", "b")
.build()
.toUriString();
// probably other method
String res = UriComponentsBuilder
.fromHttpUrl(old)
.queryParam("new", "new")
.build()
.toUriString();
System.out.println(res); // output: http://localhost?a=b&new=new
If you need to add params stored in a Map, create an instance of org.springframework.util.MultiValueMap and call queryParams or replaceQueryParams depending on your needs to add parameters or replace parameters.
// I've changed method signature to accept Map<String, List<String>>, but you can leave just String and wrap String to a singleton list
private UriComponents getUriComponent(String url, Map<String, List<String>> params) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
.queryParams(new LinkedMultiValueMap<>(params));
return builder.build();
}

Dynamically creatre response structure

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) ;

Forward a request with the exact same json body

I have a SpringBoot application which simply acts as a middleman. It receives an API request in JSON and forwards this to another server S by calling S's API with the exact same body.
I was exploring the solutions and came across a solution which involved the usage of RestTemplate and MultiValueMap. However, since the json body contains objects rather than simple String, I believe I have to create a DTO with corresponding POJO for the solution to work.
May I ask is the above the only solution, or there is a simple way to forward the request over and get back the response?
Even complex and nested JSON objects can be taken into a Map with key as String and value as Object.
I believe you should just use such a map as your request body and transfer the same to another api.
The middleman server can expose a endpoint that accepts a #RequestBody of Object and
HttpServletRequest then use RestTemplate to forward it to the remote server.
The middleman
#RestController
#RequestMapping("/middleman")
public class MiddleManRestController {
private RestTemplate restTemplate;
#PostConstruct
public void init() {
this.restTemplate = new RestTemplate();
this.restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(this.restTemplate.getRequestFactory()));
}
#RequestMapping(value = "/forward", method = RequestMethod.POST)
public ResponseEntity<Object> forward(#RequestBody Object object, HttpServletRequest request) throws RestClientException {
//setup the url and path
final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("Remote server URL").path("EnpointPath");
//add query params from previous request
addQueryParams(request, builder);
//specify the method
final RequestEntity.BodyBuilder requestBuilder = RequestEntity.method(HttpMethod.POST, builder.build().toUri());
//add headers from previous request
addHeaders(request, requestBuilder);
RequestEntity<Object> requestEntity = requestBuilder.body(object);
ParameterizedTypeReference<Object> returnType = new ParameterizedTypeReference<Object>() {};
//forward to the remote server
return this.restTemplate.exchange(requestEntity, returnType);
}
private void addHeaders(HttpServletRequest request, RequestEntity.BodyBuilder requestBuilder) {
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
requestBuilder.header(headerName, headerValue);
}
}
private void addQueryParams(HttpServletRequest request, UriComponentsBuilder builder) {
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap != null) {
parameterMap.forEach((key, value) -> queryParams.addAll(key, Arrays.asList(value)));
}
builder.queryParams(queryParams);
}
}

Sending raw JSON using Postman value is null

A pleasant day.
I am having trouble with simply displaying string in raw JSON format using Postman.
This is what I have in my Java code:
#RestController
public class HeroController {
#RequestMapping(method = {RequestMethod.POST}, value = "/displayHero")
#ResponseBody
public Map<String, String> displayInfo(String name){
//System.out.println(name);
Map<String, String> imap = new LinkedHashMap<String, String>();
map.put("hero", name);
return imap;
}
}
Every time I test this in Postman, I always get null (again if I am using raw format):
{
"hero": null
}
But using form-data, on the other hand, displays just what I entered.
{
"hero": "wolverine"
}
Any information, or should do in Postman to make this raw format works instead of form-data? By the way, the raw format value is JSON(application/json), and in the Header Tab, the value of Content-Type is application/json; charset=UTF-8.
Thank you and have a nice day ahead.
Try the following code for consuming the request body as JSON, in spring boot:-
#RequestMapping(value = "/displayHero", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String displayInfo(HttpEntity<String> httpEntity) {
String json = httpEntity.getBody();
// json contains the plain json string
// now you can process the json object information as per your need
// and return output as per requirements.
return json;
}
This code will accept json body of POST Request and then return it as response.

Creating a Java object through Ajax

I'm new to ajax and JSON (and programming in general), but what I'm trying to do is use ajax to convert my JSON object into a Java object but I keep getting "Required LocationsList parameter 'myJSONObject' is not present". Am I expecting too much of ajax here? I was told the fields in my LocationsList would be filled in automatically.
Ajax stuff:
function getAffectedFlowsForLocation(myJSONObject) {
//JSON.stringify(myJSONObject) looks like {"stations":["111", "222"],"stationGroups":["333"],"others":[]}
$.ajax({
url : baseUrl + "/locations/getFlowsAffectedByDelete",
type : "GET",
data : "{'myJSONObject':'" + JSON.stringify(myJSONObject)+ "'}",
dataType : "json",
contentType: 'application/json',
async : false,
success:function(data){
displayAffectedFlows(data);
},
error : function(){
// error is handled by global ajaxError()
success = false;
}
});
}
In the controller:
#RequestMapping(value = "/getFlowsAffectedByDelete", method = RequestMethod.GET)
#ResponseBody
public List<Map<String, String>> getFlowsAffectedByDelete(#RequestParam(value = "myJSONObject") final LocationsList locations) {
//returns a List<Map<String, String>>
}
My LocationsList:
public class LocationsList {
private List<Integer> stations;
private List<Integer> stationGroups;
private List<Integer> others;
//Getters and setters
}
Let me know if anything needs clarification.
Thanks
use
#RequestBody LocationsList locations
instead of
#RequestParam
I think you should change the way you are using the data attribute. From the jQuery documentation:
Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests.
So, it should be an object:
data : { myJSONObject : JSON.stringify(myJSONObject) },
or a string:
data : "myJSONObject=" + JSON.stringify(myJSONObject),

Categories