I am trying to get simple string from request body but keep getting errors
Handler:
#RestController
public class GreetingHandler {
public Mono<ServerResponse> hello(ServerRequest request) {
String contentType = request.headers().contentType().get().toString();
String body = request.bodyToMono(String.class).toString();
return ServerResponse.ok().body(Mono.just("test"), String.class);
}
}
Router:
#Configuration
public class GreetingRouter {
#Bean
public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
return RouterFunctions
.route(RequestPredicates.POST("/hello"),greetingHandler::hello);
}
}
Request works i can see the contenType (plainTexT) and i get the response in postman but no way i cant get to request body. The most common error i get is MonoOnErrorResume. How do i convert the body from request into String?
You will have to block to get to the actual body string:
String body = request.bodyToMono(String.class).block();
toString() will just give you the string representation of your Mono object.
Here is what block does:
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#block--
Update:
I wasn't aware that blocking on the http thread is not possible (anymore?).
Here is an adapted version of your hello controller method that prints "Hello yourInput" on the console and also returns that string in the response.
public Mono<ServerResponse> hello(ServerRequest request) {
Mono<String> requestMono = request.bodyToMono(String.class);
Mono<String> mapped = requestMono.map(name -> "Hello " + name)
.doOnSuccess(s -> System.out.println(s));
return ServerResponse.ok().body(mapped, String.class);
}
Can you use #RequestBody annotation?
public Mono<ServerResponse> hello(#RequestBody String body, ServerRequest request) {
String contentType = request.headers().contentType().get().toString();
return ServerResponse.ok().body(Mono.just("test"), String.class);
}
Related
#RequestMapping(value="/postdata", method= RequestMethod.POST, consumes="application/json")
public String postdata(#RequestBody String test, #RequestBody String data) {
logger.info("password reset Request " + requestbody.get("test"));
logger.info("password reset Request " + data);
return "Hello";
}
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String mail.controller.EmailNotifictaionController.postdata(java.lang.String,java.lang.String)]
My Input in the SOAPUI is
{
"test":"my",
"data":"god"
}
You are using two #RequestBody which is not the correct way as one request can only have one request body.
You can either combine both the request bodies into single request body or you can build a wrapper class having both the test and data variables like:
public class Body {
public String test;
public String data;
// You can also keep them private and have getters/setters.
}
and use this class in the API method argument
#RequestMapping(value="/postdata", method= RequestMethod.POST, consumes="application/json")
public String postdata(#RequestBody Body body) {
logger.info("password reset Request " + body.test);
logger.info("password reset Request " + body.data);
return "Hello";
}
You can try this way, it should work.
My application under tests has endpoint defined like below:
#ResponseBody
#RequestMapping(value = "maxsize", method = RequestMethod.POST)
public ResponseEntity<Void> changeMaxQuoteSize(#RequestBody DataRequest dataRequest,
#AuthenticationPrincipal UserProfile userProfile) {
orderManager.scheduleUpdateCurrencyConfigRules(dataRequest.getCurrency(),
(c) -> c.setMaxQuoteSize(dataRequest.getMaxSize()))
return ResponseEntity.status(HttpStatus.OK).build();
}
I want to sent message to it using rest-assured but my question is how to map request body to DataRequest object ?
I tried it that way:
class DateRq {
private String curpair;
private Double maxQuoteSize;
public DateRq(String curpair, Double maxQuoteSize) {
this.curpair = curpair;
this.maxQuoteSize = maxQuoteSize;
}
}
#Test
public void test() {
String endpoint = "http://127.0.0.1:8095/api/maxsize";
DateRq request = new DateRq(TICKER_SYMBOL, 5_000_000D);
Response response = RestAssured.given()
.when()
.body(request)
.post(endpoint);
assertEquals(200, response.getStatusCode());
}
but receive such error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com...PMTest$DateRq and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
I tried with some kind of JSON but we didn't receive any response:
#Test
public void test() {
String endpoint = "http://127.0.0.1:8095/api/maxsize";
String request = new JSONObject()
.put("curpair", TICKER_SYMBOL)
.put("maxQuoteSize", 5_000_000D)
.toString();
Response response = RestAssured.given()
.when()
.body(request)
.post(endpoint);
assertEquals(200, response.getStatusCode());
}
Have you tried code like this?
DateRq request = new DateRq(TICKER_SYMBOL, 5_000_000D);
Response response = RestAssured.given()
.body(request)
.when()
.post(endpoint);
I'm performing Integration testing in below getPlanPreferenceRules endpoint and the parameters values are not passed over to preferences-admin-service. However, when i try to test the same in postman it works fine.
Below is the code that performs REST call.
#Override
public ServiceClientResponse<GetPlanPrefRulesResponse> getPlanPrefereneRules(GetPlanPrefRulesRequest parameters,
String currentUserId, String correlationId, String requestorApp) {
RequestEntity<?> targetRequest =
createGetPlanPrefRulesRequest(parameters, currentUserId, correlationId, requestorApp);
return this.restClientService.exchangeAndParse(
this.getRulesRestTemplate, targetRequest, new TypeReference<GetPlanPrefRulesResponse>(){});
}
Below is the Spring controller service:
#GetMapping(value = PATH_PLAN_PREF_RULES)
public ResponseEntity<?> getPlanPrefRules(
#Valid final GetPlanPrefRulesRequest request )
{
log.info("getPlanPrefRulesDataRequest: Entering: GetPlanPrefRulesRequest={}", request);
return ResponseEntity.ok(this.getPlanPreferenceRulesService.getPlanPrefRules(request));
}
Below is the code that creates the request for the REST call:
protected RequestEntity<?> createGetPlanPrefRulesRequest(
final GetPlanPrefRulesRequest parameters,
final String currentUserId,
final String correlationId,
final String requestorApp)
{
URI targetUri = UriComponentsBuilder.fromUri(this.preferencesServiceUri)
.path(this.rulesPath).build().toUri();
HttpHeaders targetHeaders = createHeaders(
currentUserId,
correlationId,
requestorApp);
return new RequestEntity<>(
parameters,
targetHeaders,
HttpMethod.GET,
targetUri);
}
I am not sure what i am missing. Any inputs appreciated!
Update this method
#GetMapping(value = PATH_PLAN_PREF_RULES)
public ResponseEntity<?> getPlanPrefRules(
#RequestBody #Valid final GetPlanPrefRulesRequest request )
{
log.info("getPlanPrefRulesDataRequest: Entering: GetPlanPrefRulesRequest={}", request);
return ResponseEntity.ok(this.getPlanPreferenceRulesService.getPlanPrefRules(request));
}
I have the following code:
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
public ResponseEntity<?> api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
String response="{SOME_JSON}";
URI callbackURL = new URI("http://otherAPIEnv/api2");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(callbackURL);
return new ResponseEntity<String>(response,httpHeaders, HttpStatus.OK);
}
I tried the above code, but when I hit the api1 through my curl I get the response on the same machine, but I want the response to be redirected to api2 at otherAPIEnv machine.
Could someone please suggest how to achieve this kind of request and response?
When you send a request to a URL it should respond to the same otherwise client will be in waiting for it until it times out.
So, the approach should be different in this scenario.
First, in your main rest API you have to send a response code to release the client.
Then, in the API method you have to call another method asynchronously which calls api2 and performs the desired operation.
Here is a simple example.
#Autowired
API2Caller api2Caller;
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
#ResponseStatus(HttpStatus.ACCEPTED)
public void api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
api2Caller.callApi2(requestBody);
}
and the APICaller should look like following
#Component
public class API2Caller {
#Async
public SomeResultPojo callApi2() {
// use RestTemplate to call the api2
return restTemplate.postForObject("http://otherAPIEnv/api2", request, SomeResultPojo.class);
}
}
But you can choose your most comfortable way to perform asynchronous operation.
Look like a job for redirect.
String redirectMe() {
return "redirect:http://otherAPIEnv/api2"
}
As for the curl. You have POST mapping of the method so be sure to try it with curl -X POST... or change it to GET.
This the more modular and more generic way to do such kind of things:
public #ResponseBody ClientResponse updateDocStatus(MyRequest myRequest) {
ClientResponse clientResponse = new ClientResponse(CTConstants.FAILURE);
try {
HttpHeaders headers = prepareHeaders();
ClientRequest request = prepareRequestData(myRequest);
logger.info("cpa request is " + new Gson().toJson(request));
HttpEntity<ClientRequest> entity = new HttpEntity<ClientRequest>(request, headers);
String uri = cpaBaseUrl + updateDocUrl ;
ClientResponse serviceResponse = Utilities.sendHTTPRequest(uri, entity);
clientResponse = serviceResponse;
if (serviceResponse != null) {
if (CTConstants.SUCCESS.equalsIgnoreCase(serviceResponse.getStatus())) {
clientResponse.setStatus(CTConstants.SUCCESS);
clientResponse.setMessage(" update success.");
}
}
} catch (Exception e) {
logger.error("exception occurred ", e);
clientResponse.setStatus(CTConstants.ERROR);
clientResponse.setMessage(e.getMessage());
}
return clientResponse;
}
public static ClientResponse sendHTTPRequest(String uri, HttpEntity<ClientRequest> entity) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());
SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(CTConstants.SERVICE_TIMEOUT);
rf.setConnectTimeout(CTConstants.SERVICE_TIMEOUT);
ParameterizedTypeReference<ClientResponse> ptr = new ParameterizedTypeReference<ClientResponse>() {
};
ResponseEntity<ClientResponse> postForObject = restTemplate.exchange(uri, HttpMethod.POST, entity, ptr);
return postForObject.getBody();
}
You need to use redirect and modify the return type of your method
public String api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException {
return "redirect:http://otherAPIEnv/api2";
}
I'm trying to have a #RestController which takes a #PathVariable return a specific object in JSON format, along with proper status code. So far the way the code is, it will return the object in JSON format because it is using Spring 4 built in Jackson library by default.
However I do not know how to make it so it will give a message to the user saying we want an api variable, then JSON data, then Error code (Or success code depending if all went well). Example output would be:
Please enter api value as parameter (NOTE this can be in JSON as well if needed)
{"id": 2, "api": "3000105000" ... } (NOTE this will be the JSON response object)
Status Code 400 (OR proper status code)
The url with parameter look like this
http://localhost:8080/gotech/api/v1/api/3000105000
The code I have so far:
#RestController
#RequestMapping(value = "/api/v1")
public class ClientFetchWellDataController {
#Autowired
private OngardWellService ongardWellService;
#RequestMapping(value = "/wells/{apiValue}", method = RequestMethod.GET)
#ResponseBody
public OngardWell fetchWellData(#PathVariable String apiValue){
try{
OngardWell ongardWell = new OngardWell();
ongardWell = ongardWellService.fetchOneByApi(apiValue);
return ongardWell;
}catch(Exception ex){
String errorMessage;
errorMessage = ex + " <== error";
return null;
}
}
}
A #RestController is not appropriate for this. If you need to return different types of responses, use a ResponseEntity<?> where you can explicitly set the status code.
The body of the ResponseEntity will be handled the same way as the return value of any #ResponseBody annotated method.
#RequestMapping(value = "/wells/{apiValue}", method = RequestMethod.GET)
public ResponseEntity<?> fetchWellData(#PathVariable String apiValue){
try{
OngardWell ongardWell = new OngardWell();
ongardWell = ongardWellService.fetchOneByApi(apiValue);
return new ResponseEntity<>(ongardWell, HttpStatus.OK);
}catch(Exception ex){
String errorMessage;
errorMessage = ex + " <== error";
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
}
Note that you don't need #ResponseBody on a #RequestMapping method within a #RestController annotated class.
The idiomatic way would be to use an exception handler instead of catching the exception in your regular request handling method. The type of exception determines the response code. (403 for security error, 500 for unexpected platform exceptions, whatever you like)
#ExceptionHandler(MyApplicationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleAppException(MyApplicationException ex) {
return ex.getMessage();
}
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleAppException(Exception ex) {
return ex.getMessage();
}