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

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"
}

Related

How to write tescase for webclient onstatus method

I am new to spring webclient and i have written a generic which can be used to consume rest apis in my application:
private Function<ClientResponse, Mono<? extends Throwable>> errorStrategy() {
return response -> {
return response.bodyToMono(Errors.class).flatMap(errorResponse -> {
log.info("Track Error ----> {}", errorResponse.getErrorCode());
Errors errors = new Errors(errorResponse.getErrorMsg());
return Mono.error(errors);
});
};
}
public Mono<EnterpriseSearchResponse> getCustomerID(EnterpriseSearchRequest searchRequest) {
Mono<EnterpriseSearchResponse> response = this.client.method(HttpMethod.GET)
.uri(enterpriseSearchURI + enterpriseSearchContext)
.header("Authorization", "Bearer " + enterpriseSearchAuthToken)
.accept(new MediaType[] { MediaType.APPLICATION_JSON }).bodyValue(searchRequest).retrieve()
.onStatus(HttpStatus::is5xxServerError, errorStrategy())
.onStatus(HttpStatus::is4xxClientError, errorStrategy()).bodyToMono(EnterpriseSearchResponse.class);
return response;
}
i wanted to write junit test case for if consumed rest-api return 404 or 500 error.
can someone suggest how to achieve that?

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

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

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

Spring boot - Request method 'POST' not supported.

please i have this error when trying to create a customer. Can some one help me? May be i am missing some thing. I have even try to change the #PostMapping to #RequestMapping till yet. Thks
My Controller code
`#PostMapping("CREATE_CUSTOMER_ENDPOINT")
#ResponseStatus(value = HttpStatus.OK)
#ApiResponses(value = {
#ApiResponse(code = 201, message = "The Customer was Created", response = CustomerDto.class),
#ApiResponse(code = 400, message = "Bad Request", response = ResponseError.class),
#ApiResponse(code = 500, message = "Unexpected error")
})
public ResponseEntity createCustomer(final HttpServletRequest request, #RequestBody CustomerDto customerDto)
{
if (log.isDebugEnabled()){
log.debug("[CustomerResource] POST {} : Creating customer ", CREATE_CUSTOMER_ENDPOINT);
}
if(customerDto.getUidpk()!=null) {
ResponseError error = new ResponseError(HttpStatus.BAD_REQUEST.getReasonPhrase(), "A customer Already exist with an Uidpk");
log.error("[CustomerResource] The customer Already exist ({}) with an Uidpk", customerDto.getUidpk());
return new ResponseEntity<>(error, null, HttpStatus.BAD_REQUEST);
}
CustomerDto result = customerService.createCustomer(customerDto);
log.debug("[CustomerResource] Customer created ({})", result.getUidpk());
return new ResponseEntity<>(result, HeaderUtil.putLocationHeader(request.getRequestURL().toString() + "/" + result.getUidpk()), HttpStatus.CREATED);
} `
My endpoints
private static final String CUSTOMER_SEARCH_USER_ID_ENDPOINT = "/customers/{userId:.+}";
private static final String CREATE_CUSTOMER_ENDPOINT= "/customer";
private static final String UPDATE_CUSTOMER_ENDPOINT= "/customer";
private static final String DELETE_CUSTOMER_ENDPOINT = CREATE_CUSTOMER_ENDPOINT + "/{uidpk}";
This is the response of Postman
Postman sample
When you send JSON payloads in HTTP request, you need to specify Content-Type HTTP header with value application/json.

Error handling on controller SpringMVC

I am developing an application in jax-rs and spring mvc.
I want to notify my client each time when an default error is occured like
400, 403, 404, 405 and 415.
Controller
#Controller
#RequestMapping("/customer")
public class CustomerController {
#Autowired
CustomerService customerService;
// ........xxxxxx..............xxxxxxx................xxxxxxx.............//
#CrossOrigin
#RequestMapping(value = "/",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String fetchCustomer() throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(customerService.fetchAllCustomer());
}
// ........xxxxxx..............xxxxxxx................xxxxxxx.............//
}
Client
$http({
method: "GET",
contentType: "application/json",
url: baseUrl + '/customer'
}).success(function (response) {
console.log(response);
// you can also use
console.log(JSON.stringify(response);
}).error(function (response) {
console.log(response);
});
When i request a service from client i want to send response back with status code and custom message.
Example
When i defind method = post on controller and from client i send request as get service should return message like
error:{
Status Code: 405,
Message: Invalid Method
url: error/405
}
Check this out for reference.
Define a method for handling the specific error scenario and annotate it as #ExceptionHandler. The exception in your scenario (request method not supported) is HttpRequestMethodNotSupportedException.class. You can create more generic handler methods using Throwable, Exception etc.
In order to prevent duplication of error handling across controllers, one convenient way is to define all handlers in single class and use #ControllerAdvice on that. This way, all handlers will be applied to all controllers.
Do not return a String but return a org.springframework.http.ResponseEntity.
You can add status codes to this object
ResponseEntity<String> responseEntity = new ResponseEntity<String>("This is a response", HttpStatus.INTERNAL_SERVER_ERROR);
return responseEntity;
So your method signature will also change as below
#RequestMapping(value = "/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ResponseEntity<String> fetchCustomer() throws JsonProcessingException {
try {
String str = new ObjectMapper().writeValueAsString(customerService.fetchAllCustomer());
return new ResponseEntity<String>(str, HttpStatus.OK);
}
catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
If there is an error, you can either use controller advice or catch the exception and update the ResponseEntity appropriately

Resources