I have this method
public HTTPResult post(String url, String requestBody) throws Exception {
return HTTPPostPut(url, requestBody, HttpMethod.POST);
}
public HTTPResult HTTPPostPut(String url, String requestBody,HttpMethod httpMethod) throws Exception {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("content-type","application/json");
HttpEntity requestEntity = new HttpEntity(requestBody,headers);
try {
ResponseEntity<String> response = this.restTemplate.exchange(url, httpMethod, requestEntity, String.class);
return new HTTPResult((String) response.getBody(), response.getStatusCode().value());
} catch (ResourceAccessException var8) {
String responseBody = var8.getCause().getMessage();
JSONObject obj = new JSONObject(responseBody);
return new HTTPResult(obj.getString("responseBody"), Integer.parseInt(obj.getString("statusCode")));
}
}
Which I created for it mock and getting null pointer exception:
public void testPost() throws Exception{
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("content-type","application/json");
HttpEntity requestEntity = new HttpEntity("{blbl}",headers);
ResponseEntity<String> response = new ResponseEntity("{blbl}", HttpStatus.OK);
RestTemplate mockRestTemplate = mock(RestTemplate.class);
when(mockRestTemplate.exchange(baseUrl, HttpMethod.POST, requestEntity, String.class)).thenReturn(response);
RestAPI api = new RestAPI(mockRestTemplate);
HTTPResult res = null;
try {
res = api.post(baseUrl,"{blbl}");
} catch (IOException e) {
e.printStackTrace();
}
assertEquals(res.getResponseBody(), "{blbl}");
assertEquals(res.getStatusCode(), HttpStatus.OK.value());
}
I am getting null pointer exception when calling:
res = api.post(baseUrl,"{blbl}");
This is because the response is null.
Use an argument matcher when arranging the mock as the instance being passed to the mocked dependency is different to what is passed when the test is exercised.
This will cause the mock to return null response as expected instances do not match
Refactor the test
public void testPost() throws Exception {
//Arrange
String expected = "{blbl}";
ResponseEntity<String> response = new ResponseEntity(expected, HttpStatus.OK);
RestTemplate mockRestTemplate = mock(RestTemplate.class);
when(mockRestTemplate.exchange(eq(baseUrl), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class)))
.thenReturn(response);
RestAPI api = new RestAPI(mockRestTemplate);
//Act
HTTPResult res = api.post(baseUrl, expected);
//Assert
assertEquals(res.getResponseBody(), expected);
assertEquals(res.getStatusCode(), HttpStatus.OK.value());
}
Note the use of the any(HttpEntity.class) matcher which will allow the passed HttpEntity to be matched when invoked.
Since the use of argument matches is none or all, the eq() matcher is used for the remaining constant arguments.
Related
I am using RestTemplate to access a service.
RestTemplate rt = new RestTemplate ();
HttpEntity <String> he = new HttpEntity <String> (output, hh);
ResponseEntity <String> re = rt.exchange ("http://localhost:8080/my", HttpMethod.GET, he, String.class);
ResponseEntity has getBody and getHeaders for accessing the response.
getHeaders is already a parsed list of the header-lines.
Is there a way I can get the raw response as one big junk of bytes?
For that you will use following signature of execute method :
execute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor)
This method will execute the HTTP method to the given URL, preparing the request with the RequestCallback, and reading the response with a ResponseExtractor.
Next step is to write a custom implementation for ResponseExtractor interface and override extractData() which is responsible for extracting data from response.
Here is example.
First, write some dummy method in controller class, something like this :
#PostMapping(value = "/hello")
public String test() {
return "Hello world";
}
Then, write method which will call method above using restTemplate and return response as byte array:
#GetMapping("/test")
public void test() throws IOException {
final URI url = URI.create("http://localhost:8080/hello");
final Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json");
// Here you can set headers, request body,query parans,ect..
RequestCallback requestCallback = r -> {
//example: r.getBody().write(("username=" + username).getBytes());
r.getHeaders().setAll(headers);
};
byte[] res = restTemplate.execute(url, HttpMethod.POST, requestCallback, new ResponseExtractor<byte[]>() {
#Override
public byte[] extractData(ClientHttpResponse response) throws IOException {
byte[] data = new byte[1024];
// return data from response as byte array
response.getBody().read(data);
return data;
}
});
//write result in console
ByteArrayInputStream byteArrayInputStr
= new ByteArrayInputStream(res);
int b = 0;
while ((b = byteArrayInputStr.read()) != -1) {
char ch = (char) b;
System.out.println("Char : " + ch);
}
}
If you execute test() method, you should see output like this:
Right now I have 2 Spring App.
App A will have a controller that will receive a video file ad Multipart file and sending the file to App B via rest template.
Some Code from App A that handle sending request to App B.
#RestController
public class AppAController {
#Autowired
private final AppBService service;
#PostMapping("/sendToB")
public ResponseEntity<String> contoller(#RequestParam("file") MultipartFile file) {
String result = service.sendToB(file);
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
#Service
public class AppBService {
public String sendToB(MultipartFile file) throws ResponseStatusException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", file);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String serverUrl = "http://127.0.0.1:8090/makeFrames";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
serverUrl,
HttpMethod.POST,
requestEntity,
String.class
);
if (response.getStatusCode() != HttpStatus.OK) {
throw new ResponseStatusException(response.getStatusCode(), response.getBody());
}
return response.getBody();
}
}
And for App B, it will receive a video and extracting key frames from a video using JavaCV.
#RestController
public class ProcessorController {
#PostMapping("/makeFrames")
public ResponseEntity<String> framesExtractorController(#RequestParam("file") MultipartFile file) {
try {
File result = FramesExtractor.grabFrames(file);
return new ResponseEntity<>(result.getAbsolutePath(), HttpStatus.OK);
} catch (IOException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
public class FramesExtractor {
private static final Logger LOG = LoggerFactory.getLogger(FramesExtractor.class);
private FramesExtractor() {
}
public static File grabFrames(MultipartFile video) throws IOException {
LOG.info("Extracting Frames from the video " + video.getName());
String directoryName = video.getOriginalFilename() + "-frames-result";
directoryAssurance(directoryName);
try (FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(video.getInputStream());
Java2DFrameConverter converter = new Java2DFrameConverter()) {
frameGrabber.setImageWidth(480);
frameGrabber.setImageHeight(360);
frameGrabber.start();
Frame frame;
int i = 0;
while ((frame = frameGrabber.grabKeyFrame()) != null) {
BufferedImage bi = converter.getBufferedImage(frame);
ImageIO.write(bi, "png", new File(directoryName + "/" + String.format("%03d", i) + ".png"));
i++;
}
frameGrabber.stop();
LOG.info("Finish Extracting Frames");
return new File(directoryName);
} catch (Exception e) {
LOG.error(e.getMessage());
throw e;
}
}
}
Both app A and B have these properties set to their application.properties files.
server.port={There respective port}
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
And when i testing the application by using PostMan to send post request with video file in form-data body to App A /sendToB api i receive this error message.
"Type definition error: [simple type, class java.io.FileDescriptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile[\"inputStream\"]->java.io.FileInputStream[\"fd\"])"
So I want to ask what cause this problems and how I can fix it or sending video file to other Spring App running on other port properly.
I edited my previous answer because you can actually get parameters from a response body in a POST with #RequestParam annotation.
I would use JSON as content type. This is my solution:
public String sendToB(MultipartFile file) throws ResponseStatusException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
byte[] bytesFile = file.getBytes();
String base64String = Base64.getEncoder().encodeToString(bytesFile);
Map<String, Object> body = new HashMap<>();
body.put("file", base64String);
body.put("directoryName", file.getOriginalFilename());
body.put("videoName", file.getName())
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String serverUrl = "http://127.0.0.1:8090/makeFrames";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
serverUrl,
HttpMethod.POST,
requestEntity,
String.class
);
if (response.getStatusCode() != HttpStatus.OK) {
throw new ResponseStatusException(response.getStatusCode(), response.getBody());
}
return response.getBody();
}
For App B, the code would be:
#PostMapping("/makeFrames")
public ResponseEntity<String> framesExtractorController(#RequestBody Map<String, Object> requestBody) {
try {
String base64String = requestBody.get("file").toString();
byte[] bytesFile = Base64.getDecoder().decode(base64String);
InputStream inputStream = new ByteArrayInputStream(bytesFile);
String directoryName = requestBody.get("directoryName").toString();
String videoName = requestBody.get("videoName").toString();
File result = FramesExtractor.grabFrames(inputStream, directoryName, videoName);
return new ResponseEntity<>(result.getAbsolutePath(), HttpStatus.OK);
} catch (IOException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
public static File grabFrames(InputStream inputStream, String directoryName, String videoName) throws IOException {
//Your code
}
I am using Spring RestTemplate and want to make a call to another service that doesn't return any response body. So, I don't want to wait for the response. So, it's just fire and forget, and continue with the remaining code. I am thinking of creating a new Thread to do this but really not sure what's the correct approach.
If you use Java 11, java support asynchronous HTTP Client. Asynchronous client using CompletableFuture in the back. You can see javadoc.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://openjdk.java.net/"))
.timeout(Duration.ofMinutes(1))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofFile(Paths.get("file.json")))
.build();
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(response -> { System.out.println(response.statusCode());
return response; } )
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
The correct approach is to execute the async with a callback (using DeferredResult, like this (assuming we have a class someClass that we want to retrieve from the API:
#GetMapping(path = "/testingAsync")
public DeferredResult<String> value() throws ExecutionException, InterruptedException, TimeoutException {
AsyncRestTemplate restTemplate = new AsyncRestTemplate();
String baseUrl = "http://someUrl/blabla";
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
String value = "";
HttpEntity entity = new HttpEntity("parameters", requestHeaders);
final DeferredResult<String> result = new DeferredResult<>();
ListenableFuture<ResponseEntity<someClass>> futureEntity = restTemplate.getForEntity(baseUrl, someClass.class);
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<someClass>>() {
#Override
public void onSuccess(ResponseEntity<someClass> result) {
System.out.println(result.getBody().getName());
result.setResult(result.getBody().getName());
}
#Override
public void onFailure(Throwable ex) {
result.setErrorResult(ex.getMessage());
}
});
return result;
}
There are many ways you can use to fire the request using the AsyncRestTemplate
The simplest way is just like restTemplate and call exchange method:
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
JSONObject json = new JSONObject();
json.put("firstName","testUser");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<String>(json.toString(), headers);
Class<String> responseType = String.class;
ListenableFuture<ResponseEntity<String>> future = asyncRestTemplate.exchange("https://xxxxx.com/", HttpMethod.POST, requestEntity,responseType );
// If you want for the result then you can use
try {
//waits for the result
ResponseEntity<String> entity = future.get();
//prints body source code for the given URL
log.info(entity.getBody());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
If we want to play with the failure (fallback scenario) or success in that case we can use the below code :
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
JSONObject json = new JSONObject();
json.put("firstName","testUser");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<String>(json.toString(), headers);
//final DeferredResult<String> result = new DeferredResult<>();
ListenableFuture<ResponseEntity<String>> future =
asyncRestTemplate.postForEntity("https://xxxx.com", requestEntity, String.class);
future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
#Override
public void onFailure(Throwable ex) {
// insert into the table or log or some other decision
log.info(ex.getMessage());
}
#Override
public void onSuccess(ResponseEntity<String> result) {
log.info(result.getBody());
log.info("Sucess");
}
});
I try to develop a global webservice method in my spring boot project and I want to return my response body type according to request body type. Whatever I do, all response return json type.
#RestController
#RequestMapping(value = "/virtual/**", produces = {"application/json", "application/xml", "text/xml"}, consumes = MediaType.ALL_VALUE)
public class VirtualServiceGateway {
private IVirtualDocumentService virtualService = UtilsForSpring.getSingleBeanOfType(IVirtualDocumentService.class);
#RequestMapping(method = RequestMethod.GET)
public Response requestGET(HttpServletRequest request, HttpServletResponse response) {
IVirtualDocumentService docService = UtilsForSpring.getSingleBeanOfType(IVirtualDocumentService.class);
docService.findDocumentByVirtualUrl(request.getRequestURL().toString());
if (docService == null) {
return Response.status(404).type(request.getContentType()).entity("There is no anything").build();
}
return Response.status(200).type(request.getContentType()).entity("ok!").build();
}
I didn't find something what I want. But I found ResponseEntity, and we can use it like below. In there I can return entity type according to request/response headers.
It works for me, may be useful for somebody
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity requestTEST(HttpServletRequest request, HttpServletResponse response) {
// HttpGet httpGet = new HttpGet("http://www.webservicex.net/country.asmx/GetCountries"); // xml output format
HttpGet httpGet = new HttpGet("http://services.groupkt.com/country/get/all");//json output format
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse res = null;
StringBuilder resEntity = null;
try {
res = httpClient.execute(httpGet);
resEntity = new StringBuilder();
BufferedReader bf = new BufferedReader(new InputStreamReader(res.getEntity().getContent()));
List<String> lines = IOUtils.readLines(bf);
for (String line : lines) {
resEntity.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
for (Header h : res.getAllHeaders()) {
headers.add(h.getName(), h.getValue());
}
return new ResponseEntity(resEntity, headers, HttpStatus.valueOf(res.getStatusLine().getStatusCode()));
}
My rest server is generating response when I called it with rest client software. When I call it with resttemplate code mentioned above, then server generates response(print logs) but resttemplate does nothing(no next line executes after call) and prints internal error.
This is the method in my server
#ResponseBody
public ResponseEntity<Map<String, Object>> name(){......
...
return new ResponseEntity<Map<String, Object>>(messagebody, HttpStatus.OK);
}
This is the way I am calling it through restTemplate
ResponseEntity<Map> response1 = restTemplate.getForEntity(finalUrl.toString(), Map.class);
Try to use ParameterizedTypeReference instead of wildcarded Map.
It should looks like this.
ParameterizedTypeReference<Map<String, Object>> typeRef = new ParameterizedTypeReference<Map<String, Object>>() {};
ResponseEntity<Map<String, Object>> response = restTemplate.exchange(finalUrl.toString(), HttpMethod.GET, null, typeRef);
this is a example that works for me
#RequestMapping(value = "/getParametros/{instancia}", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> getParametros(#PathVariable String instancia)
{
LOG.debug("REST. Obteniendo parametros del servidor " + instancia);
Map<String, String> mapa = parametrosService.getProperties(instancia);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=UTF-8");
headers.add("X-Fsl-Location", "/");
headers.add("X-Fsl-Response-Code", "302");
ObjectMapper mapper = new ObjectMapper();
String s = "";
try
{
s = mapper.writeValueAsString(mapa);
} catch (JsonProcessingException e)
{
LOG.error("NO SE PUEDE MAPEAR A JSON");
}
if (mapa == null)
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
return new ResponseEntity<String>(s, headers, HttpStatus.OK);
}
you can Catch the HttpStatusCodeException from which you can get response in String .
below code works for me.
restTemplate.postForObject( url, jsonRequest, ResponseData.class );
catch( HttpStatusCodeException codeException )
{
String payload = codeException.getResponseBodyAsString();
System.out.println( payload );
}