Why sending a request to controller using Postman runs fine but using RestTemplate throws 500 Internal Server Error? - spring

Context
I have two controllers: /testParams and /callTestParams
Controller /testParams receives an object of type Example and I can call this controller from Postman without any problem.
Controller /callTestParams calls /testParams internally using RestTemplate but the response is a 500 Internal Server Error. I supose that the implementation of /callTestParams is equivalent to the call maded by Postman.
Here is the code:
#RequestMapping(value = "/testParams",
method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public ResponseEntity<Object> testParams(
#RequestBody Example credentials
) {
JSONObject params = new JSONObject( credentials );
System.out.println( params.get("clientId") + " from JSONObject");
System.out.println( credentials.getClientId() + " from GraphCredentials");
return new ResponseEntity<>(credentials,HttpStatus.OK);
}
#RequestMapping(value = "/callTestParams",
method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public ResponseEntity<Object> callTestParams() {
String url = "http://localhost:8080/GraphClient/testParams";
HttpHeaders headers = new HttpHeaders();
headers.set( HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE );
JSONObject params = new JSONObject();
params.put("clientId", "value1" );
RestTemplate restTemplate = new RestTemplate();
HttpEntity<?> entity = new HttpEntity<>(params,headers);
HttpEntity<Object> response = restTemplate.exchange(
url,
HttpMethod.POST,
entity,
Object.class
);
return new ResponseEntity<>(response.getBody(), HttpStatus.OK);
}
This is the response from Postman for /testParams
Headers:
(Content-Type,application/json)
Request Body:
JSON (appplication/json)
{"clientId":"value1"}
Response:
{
"clientId": "value1",
"clientSecret": null,
"tenantId": null,
"scope": null,
"grantType": null,
"microsoftLoginBaseURL": "https://login.microsoftonline.com/"
}
This is the response from Postman for /callTestParams
{
"timestamp": "2022-01-09T03:39:06.878+0000",
"status": 500,
"error": "Internal Server Error",
"message": "500 Internal Server Error",
"path": "/GraphClient/callTestParams"
}
This is the error in the console>
Forwarding to error page from request [/testParams] due to exception [JSONObject["clientId"] not found.]: org.json.JSONException: JSONObject["clientId"] not found.

In the parameter of the body of the HttpEntity constructor you need to pass params as String
HttpEntity<?> entity = new HttpEntity<>(params.toString(),headers);

Related

Spring Rest Controller API with HttpEntity getting null body when testing with Open API

I'm using Spring Boot 2.6.7 and Using Open API springdoc-openapi-ui 1.6.4. I have 2 services. From first service I'm using rest template to connect to second service.
In the first service, in rest controller api, I have used HttpEntity to get request object. The same is passed to rest template. The reason is with HttpEntity, I'm passing the request body as well as some other headers as well.
My controller method is as follows.
#PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
#Operation(summary = "API for submit", description = "Submit data")
#ApiResponses(value = { #ApiResponse(responseCode = "200", description = "OK"),
#ApiResponse(responseCode = "400", description = "Bad request", content = #Content(schema = #Schema(implementation = Failure.class))),
#ApiResponse(responseCode = "500", description = "Error", content = #Content(schema = #Schema(implementation = Failure.class))), })
public ResponseEntity<Success<SubmitOpr>> submit(HttpEntity<OperationReq> httpEntity) throws Exception {
log.info("Request Entity is {}", httpEntity);
log.info("Request Body is {}", httpEntity.getBody());
SuccessResponse<SubmitOpr> response = null;
try {
response = oprService.submit(httpEntity);
} catch (Exception e) {
log.error("Failure: {}", e.getMessage());
throw e;
}
return ResponseEntity.ok().body(response);
}
My application works fine with this. And with postman client also it works fine.
But when I use swagger UI to test, I did not get expected result. And when I debug, httpEntity.getBody() is null
If I change from HttpEntity<OperationReq> httpEntity to OperationReq httpEntity and then accordingly change subsequent service layer methods, the api works fine in swagger.
But I don't want to change that. Because I want to pass HttpEntity and another thing is there are so many similar APIs and it would be very difficult to change everywhere.
Is there a better solution to this?
I think there is no direct solution to this. If you are looking to get headers along with request body, what you can do is, you can get RequestBody and get Headers in controller and create an HttpEntity object. And that you can pass on to your service layer methods.
Change will be only in controller side and no change will be required from service layers.
#PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
#Operation(summary = "API for submit", description = "Submit data")
#ApiResponses(value = { #ApiResponse(responseCode = "200", description = "OK"),
#ApiResponse(responseCode = "400", description = "Bad request", content = #Content(schema = #Schema(implementation = Failure.class))),
#ApiResponse(responseCode = "500", description = "Error", content = #Content(schema = #Schema(implementation = Failure.class))), })
public ResponseEntity<Success<SubmitOpr>> submit(#RequestBody OperationReq operationReq, #RequestHeader MultiValueMap<String, String> headers) throws Exception {
HttpEntity<OperationReq> httpEntity = new HttpEntity<>(operationReq, headers);
log.info("Request Entity is {}", httpEntity);
log.info("Request Body is {}", httpEntity.getBody());
SuccessResponse<SubmitOpr> response = null;
try {
response = oprService.submit(httpEntity);
} catch (Exception e) {
log.error("Failure: {}", e.getMessage());
throw e;
}
return ResponseEntity.ok().body(response);
}
I had the same behavior of Swagger for HttpEntity controller method parameter. In my case the problem was that Swagger sent httpEntity as a query parameter instead body.
I have added a #io.swagger.v3.oas.annotations.parameters.RequestBody annotation and it solved the problem:
#io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
description = "Put here your feature configuration DTO in a JSON format",
content = #Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = #ExampleObject(value = "{}")
)
)
HttpEntity<String> httpEntity

Getting null body in response from feign client, even though a direct request is returning an entity

I have this Feign Client in my spring boot application :
#Component
#FeignClient(value = "apiKeyManager", url = "http://localhost:8081/", configuration = FileUploadConfiguration.class)
public interface ApiKeyClient {
#RequestMapping(method = RequestMethod.POST, value = "/api/public/getAppName", consumes = "application/json", produces = "application/json")
ResponseEntity getAppName(#RequestBody AppNameRequestDto appNameRequestDto);
}
And I have this code in my service, which calls it :
AppNameRequestDto request = new AppNameRequestDto(apiKey);
ResponseEntity verification = apiKeyClient.getAppName(request);
return verification;
The actual endpoint being called by the feign client looks like this :
#PostMapping(value = "getAppName", consumes = "application/json", produces = "application/json")
public ResponseEntity getAppName(#RequestBody AppNameRequestDto appNameRequestDto){
try {
return new ResponseEntity(apiKeyManagementService.getAppName(appNameRequestDto.getApiKey()), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity("Failed to locate application by API_KEY : " + appNameRequestDto.getApiKey(), HttpStatus.NOT_FOUND);
}
}
When I run this code - I get this response :
{
"headers": {
<REMOVED FOR BREVITY>
},
"body": null,
"statusCode": "OK",
"statusCodeValue": 200
}
But when I make the call to the underlying API directly, I get the response I am expecting - an entity with an accompanies 200 status :
{
"applicationName": "MyNewFileUploadServiceApplication6"
}

Unable to generate access token

I am getting exception while generating an Access Token using feign client. The same payload is working fine in the Postman.
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("grant_type", "client_credentials");
map.add("client_id", "67881e5b-f5d5-4085-8762-c35b7b6aeede");
map.add("client_secret", "D-85Pg3wN63dmznxa-puB_89Po~o5CsKhA");
map.add("scope", "https://graph.microsoft.com/.default");
AccessTokenResponse openIdTokenResponse = graphAPILoginFeignClient.getAccessIdToken("5494cc2e-fb14-4a2d-bb5e-bf164d9141cf",request);
Feignclient code:
#FeignClient(name = "GraphAPILoginFeignClient", url = "${graphApiLoginUrl}")
public interface GraphAPILoginFeignClient {
#PostMapping(value = "/{tenantID}/oauth2/v2.0/token",consumes = MediaType.APPLICATION_JSON_VALUE)
AccessTokenResponse getAccessIdToken(#PathVariable("tenantID") String tenantID,
#RequestBody MultiValueMap<String, Object> request);
}
Exception:
{
"timestamp": "2021-01-27T17:30:34.456+00:00",
"message": "[400 Bad Request] during [POST] to [https://login.microsoftonline.com/5494cc2e-fb14-4a2d-bb5e-bf164d9141cf/oauth2/v2.0/token] [GraphAPILoginFeignClient#getAccessIdToken(String,AuthorizationTokenRequest)]:
[{\"error\":\"invalid_request\",
\"error_description\":\"AADSTS900144: The request body must contain the
following parameter: 'grant_type'.\\r\\n
Trace ID: b8ef5f37-95f7-4427-8f0e-146a34b65000\\r\\n
Correlation ID: ... (503 bytes)]","details": "uri=/accessmanagement/allusers"
}
Same request payload working from Postman:
I had the same problem and the same code.
I was able to successfully execute the code by replacing the class method MultiValueMap::put with MultiValueMap::set.
In the LinkedMultiValueMap class, these methods are implemented differently.

spring boot rest client connection Exception:: org.springframework.web.client.HttpClientErrorException: 400 null

while i am executing below code i am getting error like
"org.springframework.web.client.HttpClientErrorException: 400 null".
but when i use postman to call this "http://localhost:2018/test" it is working.
static final String URL_EMPLOYEES = "http://localhost:2018/test";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(new MediaType[] {
MediaType.APPLICATION_JSON}));
// Request to return XML format
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("replyMsg", "str");
// HttpEntity<Employee[]>: To get result as Employee[].
HttpEntity<String> entity = new HttpEntity<String>(headers);
// RestTemplate
RestTemplate restTemplate = new RestTemplate();
// Send request with GET method, and Headers.
ResponseEntity<String> response =
restTemplate.exchange(URL_EMPLOYEES,
HttpMethod.POST, entity,String.class);
HttpStatus statusCode = response.getStatusCode();
// Status Code: 200
if (statusCode == HttpStatus.OK) {
// Response Body Data
msg=response.getBody();
if (msg != null) {
System.out.println(msg);
}
}
//my clint controller class
#RestController
public class TextController {
#RequestMapping(value="/test",method = RequestMethod.POST)
public String myData2(#RequestBody String payload) {
return "done";
}
}
any suggetions?
If you're using Jackson as your JSON parser, you can simply declare your parameter with the type TextNode. This is the Jackson type representing JSON strings.
public String updateName(#PathVariable(MY_ID) String myId, #RequestBody TextNode name) {
You can then use its asText method to retrieve its text value.
Here you are setting headers Content-Type with type JSON and passing the body of type text/String.
headers.setContentType(MediaType.APPLICATION_JSON); //setting your Content type as JSON.
So, First you need to change this to
headers.setContentType(MediaType.TEXT_PLAIN); //setting your Content type as Pure Text String.
and add some code after this line
// HttpEntity<Employee[]>: To get result as Employee[].
HttpEntity<String> entity = new HttpEntity<String>(headers);
add this code
// HttpEntity<Employee[]>: To get result as Employee[].
HttpEntity<String> entity = new HttpEntity<String>(headers);
// RestTemplate
RestTemplate restTemplate = new RestTemplate();
// Send request with GET method, and Headers.
String entity_Str = new ObjectMapper().writeValueAsString(entity);
ResponseEntity<String> response =
restTemplate.exchange(URL_EMPLOYEES,
HttpMethod.POST, entity_Str, String.class);
This might work for you.. Thanks :)

how to make a POST call to Facebook Marketing API?

I'm trying to do a POST call to Facebook's graph API from my application. The method's relevant excerpt is:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization","Bearer "+ accessToken);
HttpEntity<EventSetVM> request = new HttpEntity<>(new EventSetVM(eventSetName), headers);
Map<String, String> params = new HashMap<>();
params.put("name", eventSetName);
String url = "https://graph.facebook.com/v2.8/" +
businessManagerId +
"/offline_conversion_data_sets";
EventSetVM eventSetVM = restTemplate.postForObject(url, request, EventSetVM.class, params);
The error I'm getting from Firefox's RESTClient is
{
"timestamp": 1487779045470,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.web.client.HttpClientErrorException",
"message": "400 Bad Request",
"path": "<correct-path-with-no-parameters>"
}
What's going on here?
Graph API requires explicit "access_token" parameter in your request. You need to add your access_token parameter in your params.

Resources