Mapping rest post request to object - java

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

Related

What is the correct way of testing a POST endpoint that accepts a POJO and a MultipartFile[] attachment?

I'm new to MockMVC. I've successfully written some basic tests, but I got stuck on trying to test an use case with the endpoint that requires a POST request with two parameters - a POJO and an array of MultipartFile. The test is written as such:
#Test
public void vytvorPodnetTest() throws Exception {
var somePojo = new SomePojo();
somePojo.setSomeVariable("test_value");
var roles = List.of("TEST_USER");
var uid = "00000000-0000-0000-0000-000000000001";
MockMultipartFile[] attachments = {new MockMultipartFile("file1.txt", "file1.txt", "text/plain", "file1 content".getBytes()),
new MockMultipartFile("file2.txt", "file2.txt", "text/plain", "file2 content".getBytes())};
MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart("/some-pojo/create");
builder.with(req - {
req.setMethod("POST");
return req;
});
MvcResult result = mockMvc.perform(builder.file(attachments[0]).file(attachments[1])
.param("SomePojo", new ObjectMapper().writeValueAsString(somePojo))
.file(attachment[0])
.with(TestUtils.generateJWTToken(uid, roles)))
.andExpect(status.isOk())
.andReturn();
}
The controller method is as follows:
#PostMapping(value = "/create", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public UUID createPojo(
#RequestPart(value = "SomePojo") SomePojo somePojo,
#RequestPart(value = "attachments", required = false) MultipartFile[] attachments) {
return pojoService.create(somePojo, attachments);
}
It stops here, before reaching the service. I've tried adding the files both as a param "attachments" and like shown above, but all I get is "400 Bad Request"
Finally found the way to send the parameters as MockMultipartFile from MockMVC to the controller:
MockMultipartFile pojoJson = new MockMultipartFile("SomePojo", null,
"application/json", JsonUtils.toJSON(podnet).getBytes());
mockMvc.perform(MockMvcRequestBuilders.multipart("/some-pojo/create")
.file(pojoJson)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.with(new TestUtils().generateJWTToken(uid, roles)))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();

How to POST multipart/form-data in MockMVC?

I've created a controller that would consume a 'multipart/form-data'
#PostMapping(value="/sample")
public void sample(
#ModelAttribute("request") SampleRequest request){
// codes
}
SampleRequest object
#NotNull
private MultipartFile file;
#Pattern(regexp = "^[0-9A-Za-z]*")
private String fileName;
private String other;
And now, I will try to test it using Mock MVC but I don't know how to pass 'multipart/form-data' as content. I saw a lot of sample using JSON but not with multipart/form-data
mockMvc.perform(post(path)
.servletPath(path)
.headers(headers)
.contentType(MediaType.MULTIPART_FORM_DATA)
.content(request)) // -> How to put the multipart/form-data here
.andDo(print())
.andReturn();
Is there a way I can complete my request with multipart/form_data? Ideally it needs to be in the body of MockHttpServletRequest
MockHttpServletRequest:
HTTP Method = POST
Request URI = --path
Parameters = {}
Headers = --headers
Body = null
I've managed to do it that way:
Resource fileResource = new ClassPathResource("YOUR FILE NAME");
assertNotNull(fileResource);
MockMultipartFile firstFile = new MockMultipartFile(
"file",fileResource.getFilename(),
MediaType.MULTIPART_FORM_DATA_VALUE,
fileResource.getInputStream());
assertNotNull(firstFile);
MockMvc mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext).build();
MvcResult andReturn = mockMvc.perform(MockMvcRequestBuilders
.multipart(**YOUR URL**)
.file(firstFile)
.headers(**YOUR HEADERS**))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
I've seen this example here:
https://www.baeldung.com/spring-multipart-post-request-test

Getting String body from Spring serverrequest

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

Spring rest service not returning body

I have one rest service with following implementation -
#RequestMapping(method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
#JsonSerialize
public ResponseEntity<String> handleData(HttpMethod method, HttpServletRequest httpRequest)
throws URISyntaxException, IOException {
BackendRequest request = new BackendRequest();
request.setHttpRequest(httpRequest);
request.setMethod(method);
BackendResponse backendResponse = service.getresponse(request);
ResponseEntity<String> response = backendResponse.getResponse();
return new ResponseEntity<String>(response.getBody(), response.getHeaders(), response.getStatusCode());
// return response;
}
I am getting all the headers and response status correctly but I am not getting the json response. What is wrong here?
I am trying to do following -
https://stackoverflow.com/a/23736527/2197994
Somewhere deep inside the nested calls, I am getting the response from some other backend using spring rest template.
public BackendResponse callBackend(BackendRequest request) throws URISyntaxException, IOException {
String body = null;
ResponseEntity<String> responseEntity = null;
URI uri = new URI("http", null, "localhost", 8080, request.getRequestURL(), request.getQueryString(), null);
MultiValueMap<String, String> requestHeaders = getHeadersInfo(request.getHttpRequest());
if (HttpMethod.POST.equals(request.getMethod())) {
body = request.getHttpRequest().getReader().lines().collect(Collectors.joining(System.lineSeparator()));
responseEntity = restTemplate.exchange(uri, request.getMethod(),
new HttpEntity<String>(body, requestHeaders), String.class);
} else if (HttpMethod.GET.equals(request.getMethod())) {
responseEntity = restTemplate.exchange(uri, request.getMethod(),
new HttpEntity<String>(body, requestHeaders), String.class);
} else {
LOG.warn("Method:{} not supported yet", request.getMethod());
}
BackendResponse response = new BackendResponse();
response.setResponse(responseEntity);
return response;
}
BackendResponse backendResponse = service.getresponse(request) could be the problem. Could you post the content of the method ?

SpringBoot how to Send response to other URL

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

Categories

Resources