ResponseEntity<MyDTO> is Returning Missing/Incomplete JSON - spring

I'm seeing an issue with JSON cut-off/missing/incomplete when getting a response back from my service in Spring Boot.
Example:
ResponseEntity<MyDTO> responseEntity = myService.getMyDTO();
return responseEntity;
public class MyService {
public ResponseEntity<MyDTO> getMyDTO() {
return restTemplate.exchange(requestUrl, HttpMethod.GET, new HttpEntity<>(httpHeaders), MyDTO.class)
}
}
When debugging and inspecting the body of ResponseEntity, which is MyDTO instance, it contains
all the expected fields.
public class MyDTO {
private Information information;
private Address address;
}
public class Information {
private String firstName;
private String lastName;
}
public class Address {
private String streetName;
}
Debugging:
MyDTO
body
information
firstName > "John"
lastName > "Doe"
address > null
Expected JSON:
{
"information": {
"firstName": "John",
"lastName": "Doe"
},
"address: null
}
Actual JSON:
{
"information": {
"firstName": "Jo
Yes, the ending brackets are even missing from the response JSON.
Note: address is null because the response from upstream service returns a 400 and we map that response to our DTO (MyDTO). Initially, I thought this was the problem until debugging confirmed that the mapping was done correctly.
Here's what's really strange. If I take that ResponseEntity's body and put it in another ResponseEntity, than it returns fine and works. The service even returns faster, which is weird too.
ResponseEntity responseEntity = myService.getMyDTO();
return new ResponseEntity(responseEntity.getBody(), responseEntity.getStatusCode());
Anyone knows what's going on? Is it a networking, Spring Boot, or my code issue? Why would returning a new ResponseEntity fix the issue?

Found the issue. The upstream service contains a 'Content-Length' header, which is too small and causing the missing/incomplete JSON on client-side.

This is happening because you are calling the restTemplate.exchange() method; if you use the getForObject() method (in case of post call postForObject()) instead it will work.
This method will first create the MyDTO class object and then you need to add that object in the ResponseEntity object to return it.
Example:
MyDTO myDtoObject = restTemplate.getForObject(requestUrl, MyDTO.class);
return new ResponseEntity<>(myDtoObject, HttpStatus.OK);

Related

How to write #ApiResponse which may return a Class or a List of that class using OpenAPI 3 Swagger in Spring Boot

As written in documentation we can use anyOf with #Schema if we want to define multiple responses.
#ApiResponse(responseCode = "201", description = "OK",
content = #Content(schema = #Schema(anyOf = {Product.class, Activity.class})))
My controller returns either a Product or a List<Product>. I would like to specify this in my OpenAPI 3 documentation.
I would like to know if it's possible.
If Yes, then how?
If No, then is there any workaround?
I don't only want to specify List.class. I want to specify List<Product>.
P.S.:- Searching on Google didn't get me any results that I can use.
Ok, thats a tough one.
Basically if you really want to return a List of Objects or one Object, then you can create a basic interface like this
public interface Response {
}
And then you can create your Object, which implements the response
public class Hello implements Response {
private String message;
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
Finally we can create the List of our Object. For that we need to create a class, which extends the ArrayList and implements our Interface
public class HelloList extends ArrayList<Hello> implements Response {
}
After that we can just set our schema as implementation
#ApiResponse(responseCode = "200", description = "hello world", content = #Content(mediaType = "application/json", schema = #Schema(implementation = Response.class)))
On the Clientside you need to check the instance of the Response-Object, so that you can parse either the Object or the List
Response response = someCall();
if (response instanceof Hello) {
System.out.println(processHello((Hello) response);
}
if (response instanceof HelloList) {
System.out.println(processHelloList((HelloList) response);
}
This example works, but its very very complex und confusing. A better way to design your api, would be to return just the list. I don't see the benefit to seperate one object or the list of it.

Does Springboot #RequestParam support List<Object> params in get request

Springboot #RequestParam annotation can pass basic list parameters, just like:
#GetMapping("param")
public String requestParamDemo(#RequestParam("list")List<Long> list) {
System.out.println(list.toString());
return list.toString();
}
and in postman, GET request localhost:8998/param?list=1,3,100 is works, "1,3,100" can be converted to List, but how or if Springboot #RequestParam support custom Generics such as below:
#GetMapping("objlist")
public String paramWithObjList(#RequestParam("objList")List<AaParam> objList) {
System.out.println("objList = " + objList);
return objList.toString();
}
import lombok.Data;
#Data
public class AaParam {
private int id;
private String name;
}
postman request: GET url: localhost:8998/objlist?objlist=[{id: 1, name: "aa"},{id: 2, name: "bb"}]
I tested in local and it didn't work.
Want to know if #RequestParam can do that or any alternative way to implement it.
Thanks!
Hope this suggestion holds good for your requirement.
I would suggest going with #RequestBody code will look like this
#PostMapping(path = "/objlist", consumes = "application/json", produces = "application/json")
public String paramWithObjList(#RequestBody List<AaParam> objList) {
System.out.println("objList = " + objList);
return objList.toString();
}
Note: please add some ObjectPaser in your actual logic (for example Jackson )
Postman request would be like this

Why does RestTemplate returns ArrayList<LinkedHashMap> instead of real list of model type?

It is a Springboot project. The code snip is as below. At line 59, the desired retrun type for restTemplate.getForEntity is List<Template>. While debugging, I find that the actual return type is an ArrayList contains many LinkedHashMap.
While LinkedHashMap is not sub class of Template. I don't know why the expect result type and the actual result type match.
Could anyone tell why it doesn't return ArrayList<Template>, instead of ArrayList<LinkedHashMap>? Thanks.
Template is an model defined in our project.
public class Template {
private String id;
private String name;
private String content;
xxx getters and setters
}
And it is a controller where the resttemplate is invoked.
#PostMapping(value = "/getTemplatesByGroup", produces = "application/json;charset=UTF-8")
#ResponseBody
public EUDataGrid<Template> getTemplatesByGroup(#RequestParam(defaultValue = "-1") Integer groupId) {
EUDataGrid<Template> grid = new EUDataGrid<>();
xxxx
List<Template> list = restTemplate.getForEntity(urlFullTemplates, ArrayList.class).getBody();
xxxx
return grid;
}
Json result format as below
[
{
"id": 1788,
"name": "xxxx",
"content": "xxxxx."
},
{
"id": 1787,
"name": "xxxxx",
"content": "xxxx"
}
]
Edit:
I googled a lot for this issue again. It is a common problem. There are similar scenarios some guys also encountered. I add the link in the foot of this post.
It seems that this is a bug of RestTemplate to handle generic properly. And there are ways to resolve this.
Here I want to know, why it doesn't throw exception when restTemplate returns ArrayList<LinkedHashMap> and assign it to List<Template>? They are differnt types. This is some kind of like assgin an int to a string.
I guess there is some magic with generic type. Could someone tell more about this? Thanks.
Unable to get a generic ResponseEntity<T> where T is a generic class "SomeClass<SomeGenericType>"
Using Spring RestTemplate in generic method with generic parameter
RestTemplate: how to get generic List response
Here is the problem in JSON response you are getting List<Template> or Array of Template, but in the responseType you just specified ArrayList where jackson doesn't know which type of ArrayList it is
limitation See the limitation
getForEntity(URI url, Class<T> responseType)
This sends a request to the specified URI using the GET verb and converts the response body into the requested Java type. This works great for most classes, but it has a limitation: we cannot get lists of objects.
One way is simple just specify Array type
Template[] list = restTemplate.getForEntity(urlFullTemplates, Template[].class).getBody();
Or use the exchange method

DTO has only null with GET request params, but not POST #RequestBody

I'm trying to get my query params in a DTO like in this question but my DTO has always null value.
Is there anything wrong in my code ? I made it as simple as possible.
Queries:
GET http://localhost:8080/api/test?a=azaz => null
POST http://localhost:8080/api/test with {"a":"azaz"} => "azaz"
Controller with a GET and a POST:
#RestController
#RequestMapping(path = {"/api"}, produces = APPLICATION_JSON_VALUE)
public class MyController {
// GET: dto NOT populated from query params "?a=azaz"
#RequestMapping(method = GET, path = "test")
public #ResponseBody String test(TestDto testDto){
return testDto.toString(); // null
}
// POST: dto WELL populated from body json {"a"="azaz"}
#RequestMapping(method = POST, path = "test")
public #ResponseBody String postTest(#RequestBody TestDto testDto){
return testDto.toString(); // "azaz"
}
}
DTO:
public class TestDto {
public String a;
#Override
public String toString() {
return a;
}
}
Thanks !
Full Spring boot sample to illustrate it
The problem is that you are missing setter for the field.
public void setA(String a) {
this.a = a;
}
should fix it.
I'm assuming that you have done required configuration like having Jackson mapper in the class path, consume json attribute, getter and setter in DTO classes etc.
One thing missed here is, in RequestMapping use value attribute instead of path attribute as shown below
#RequestMapping(method = POST, value= "/test", consumes="application/json")
public #ResponseBody String postTest(#RequestBody TestDto testDto){
return testDto.toString();
}
And, make sure that you set content-type="application/json" while sending the request
I think what you are trying to do is not possible. To access the query Parameter you have to use #RequestParam("a"). Then you just get the String. To get your object this way you have to pass json as Parameter. a={"a":"azaz"}
Kind regards

Can we use #RequestParam along with #RequestBody in Spring Controller

public ResponseEntity<String> action(#RequestParam(value = "id") final String id,#RequestBody item item)
throws IllegalAccessException {
System.out.println("");
}
I get Bad request error whenever I hit this URL.
Request JSON:
{
"id": "rw121232323e",
"item":{
"code": "shirt",
"qty":10
}
}
But the code works when I have only #RequestBody in my controller method.
Wanted to know if #RequestParam and #RequestBody can be used together.
With the example json you provided, the id is part of the body, not a request parameter. Also, the default value for the "required" attribute of #RequestParam is true, and this I suspect is the reason you are getting the Bad Request response. Either you specify the parameter in the url (by putting ?id=yourId at the end of it), or you specify the request parameter like this :
#RequestParam(required = false) String id

Resources