Spring REST - pass PDF through a request - spring

I have got 2 Spring Boot Applications...
First application should generate a PDF and return it... Second Application is calling first application to get the pdf and return it to user...
Here i am generating a pdf.
#RequestMapping(value = "/html2pdf", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> report(#RequestParam() String htmlContent) {
try {
ByteArrayInputStream pdfDocument = pdfGenerator.generatePDF(htmlContent);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.add("Content-Disposition", "inline; filename=generated.pdf");
return ResponseEntity
.ok()
.headers(headers)
.body(new InputStreamResource(pdfDocument));
} catch (IOException e) {
return ResponseEntity.badRequest().build();
}
}
Here i am trying to make a call against first application and return the pdf...
#RequestMapping(value = "/pdfreport", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> report() {
ResourceHttpMessageConverter resourceHttpMessageConverter = new ResourceHttpMessageConverter();
List<MediaType> supportedApplicationTypes = new ArrayList<>();
MediaType pdfApplication = new MediaType("application", "pdf");
supportedApplicationTypes.add(pdfApplication);
resourceHttpMessageConverter.setSupportedMediaTypes(supportedApplicationTypes);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(resourceHttpMessageConverter);
RestTemplate pdfGenerator = new RestTemplate();
pdfGenerator.setMessageConverters(messageConverters);
ResponseEntity<InputStreamResource> response = pdfGenerator.getForEntity("http://localhost:1080/pdf-generator/html2pdf?htmlContent=<h2>HUHU</h2>", InputStreamResource.class);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "inline; filename=generated.pdf");
headers.setContentType(MediaType.APPLICATION_PDF);
return ResponseEntity
.ok()
.headers(headers)
.body(response.getBody());
}
in this constellation i get following error:
java.io.IOException: stream is closed
I have already tried it with ResponseExtractor...
What is the problem here? How can i solve it more easy?
Thanks

InputStreamResource Should only be used if no other specific Resource implementation is applicable. In particular, prefer ByteArrayResource or any of the file-based Resource implementations where possible.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/io/InputStreamResource.html

Related

SpringBoot: HttpClientErrorException$BadRequest: 400: [no body] when calling restTemplate.postforEntity or restTemplate.exchange

I am trying to hit a restful endpoint from my springboot application using restTemplate.exchange and restTemplate.postForEntity and I am getting 400 Bad request [no body] exception.
I tried every possible solution to figure out the issue. From the same code I used HttpURLConnection to hit the endpoint and it works fine. I am able to get valid response. I also tried hitting the endpoint from Postman and it works fine
Below is endpoint code
#RestController
#RequestMapping("auth")
#Slf4j
public class AuthController {
#PostMapping(value = "/as/resourceOwner",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Token> getToken(#RequestParam(required = false)MultiValuedMap<?, ?> params) {
log.info("token endpoint");
return new ResponseEntity<>(new Token(), HttpStatus.OK);
}
}
The below code with HttpURLConnection class works fine
public class Test {
public static void main(String[] args) {
try {
URL url = new URL("http://localhost:8080/fc-services-mock-auth/fmrco/as/resourceOwner");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE);
OutputStream writer = urlConnection.getOutputStream();
writer.write("a=a&b=b".getBytes(StandardCharsets.UTF_8));
writer.flush();
writer.close();
BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
System.out.println(e);
}
}
}
But the below code with restTemplate does not work
try {
HttpEntity httpEntity = new HttpEntity<>(createBody(), createHeaders());
ResponseEntity<String> response = restTemplate.exchange(new URI(endpointConfig.getTokenServiceUrl()),
HttpMethod.POST, httpEntity, String.class);
HttpEntity<MultiValueMap<String, String>> request =
new HttpEntity<>(createBody(), createHeaders());
ResponseEntity<TokenResponse> tokenResponse = restTemplate.postForEntity(
new URI(endpointConfig.getTokenServiceUrl()),
request,
TokenResponse.class);
logger.debug(" token process completed");
return tokenResponse.getBody();
} catch (Exception e) {
throw new TokenException(" token error: ", e);
}
private HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE);
return headers;
}
private MultiValueMap createBody() {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("client_id", applicationConfig.getTokenClientId());
map.add("client_secret", applicationConfig.getTokenClientSecret());
map.add("grant_type", "password");
map.add("username", applicationConfig.getTokenUsername());
map.add("password", applicationConfig.getTokenPassword());
return map;
}
Can anyone please tell me what is wrong here?
Additionally I have written another GET restful endpoint like below and while hitting the endpoint using exchange I still get the same error.
#GetMapping(value = "test", produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> test() {
return new ResponseEntity<>("Hello", HttpStatus.OK);
}
try {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_PLAIN));
HttpEntity<MultiValueMap<String, String>> entity =
new HttpEntity<>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
"http://localhost:8080/context/path",
HttpMethod.GET, entity, String.class);
} catch (Exception e) {
System.out.println(e);
}
Also, intentionally I tried making a POST call to the get endpoint to see if it returns 405 Method not allowed. But to my wonder it still returned 400 Bad Request no body.
I finally figured it out. The restTemplate is configured to use a proxy. I removed the proxy and it is working fine now.
You can try something like:-
public void fetchAuthenticationToken() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("client_id", applicationConfig.getTokenClientId());
map.add("client_secret", applicationConfig.getTokenClientSecret());
map.add("grant_type", "client_credentials");
map.add("username", applicationConfig.getTokenUsername());
map.add("password", applicationConfig.getTokenPassword());
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
String tokenEndPoint = "{your endpoints URL}";
ResponseEntity<TokenResponse> responseEntity = testRestTemplate.exchange(tokenEndPoint,
HttpMethod.POST, entity, TokenResponse.class, new HashMap<String, String>());
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
TokenResponse token = responseEntity.getBody();
assertThat(token).isNotNull();
System.out.println("Auth Token value is "+ token)
}

no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [multipart/form-data;boundary=XXX;charset=UTF-8]

I am doing multipart formdata post call from UI to upload a file.
From UI, the call goes to component A controller which you can consider it as a common component or bus which just read the stream pass the stream to other component B controller through rest call which uploads the file and sends back metadata about uploaded file in json format.
Issue: The call successfully goes till component B controller, file got uploaded but when the response of component B reaches component A, I am getting below error
"message" : "Could not extract response: no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [multipart/form-data;boundary=HWhdmg6KNJw_kaP4wWnLyoAb9htc8StF4;charset=UTF-8]"
Can someone help pls?
Components A code
#PostMapping(value = "/v3/documents",consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity uploadMultipartDocument(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) {
ResponseEntity<?> responseEntity = null;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set(HznAppsHeaderConstant.DOCUMENT_TYPE, HznAppsUtil.getHeaderFromCustomHeaderParameterProvider(customHeaderParameterProvider, HznAppsHeaderConstant.DOCUMENT_TYPE));
InputStream stream =null;
MultiValueMap<String, Object> params= new LinkedMultiValueMap<String, Object>();
try{
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterStream = upload.getItemIterator(req);
while (iterStream.hasNext()){
FileItemStream item = iterStream.next();
stream = item.openStream();
if (!item.isFormField() && item.getFieldName().equalsIgnoreCase("file")) {
InputStreamResource inputStream = new InputStreamResource(stream) {
#Override
public String getFilename() {
return item.getName();
}
#Override
public long contentLength() {
return -1;
}
};
params.add("file", inputStream);
break;
}
}
final HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(params, headers);
restTemplate.getInterceptors().clear();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
responseEntity = restTemplate.exchange("Component B URL", HttpMethod.POST, httpEntity, Object.class);
} catch (Exception e) {
e.printStackTrace();
}
return responseEntity;
}
Component B code
#RequestMapping(value = "/api/v3/documents", produces = {"application/json"}, consumes = {"multipart/form-data"}, method = RequestMethod.POST)
public ResponseEntity uploadMultipartDocument(HttpServletRequest request) {
ResponseDocument responseDocument = null;
try {
responseDocument = documentsService.uploadMultiPartDocument(request,documentType); // Doing Soap call to upload document and its works fine in this method
} catch (Exception e) {
e.printStackTrace();
}
return new ResponseEntity<>(responseDocument, HttpStatus.OK);
}
Thanks

Spring Boot RestTemplate: Bad request when directly copying from postman

So I have an API request where I am copying the details directly from postman where it works. I am however getting a bad request error.
#Service
public class GraphApiService {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Autowired
RestTemplate restTemplate;
#Autowired
Constants constants;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public ResponseEntity<String> getAccessTokenUsingRefreshToken(Credential cred) throws IOException{
try {
//https://learn.microsoft.com/en-us/graph/auth-v2-user
// section 5. Use the refresh token to get a new access token
String url = "url";
JSONObject body = new JSONObject();
body.put("grant_type", "refresh_token");
body.put("client_id", "clientid");
body.put("scope","User.Read offline_access Files.Read Mail.Read Sites.Read.All");
body.put("redirect_uri", "http://localhost");
body.put("client_secret","secret");
body.put("refresh_token", "token");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> request = new HttpEntity<String>(body.toString(), headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, request,String.class);
return response;
}
catch(HttpClientErrorException e){
logger.error(e.getResponseBodyAsString());
logger.error(e.getMessage());
return null;
}
}
I would appreciate any help. The bad request error message from microsoft graph isn't a descriptive one that will help
You're sending JSON payload with FORM_URLENCODED header.
Either you need to check if API accepts json payload, if so you need to change content-type to application/json or you can post form data as follows.
public ResponseEntity<String> getAccessTokenUsingRefreshToken(Credential cred) throws IOException{
try {
//https://learn.microsoft.com/en-us/graph/auth-v2-user
// section 5. Use the refresh token to get a new access token
String url = "url";
MultiValueMap<String, String> multiValueMap= new LinkedMultiValueMap<String, String>();
multiValueMap.add("grant_type", "refresh_token");
multiValueMap.add("client_id", "clientid");
//.....
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(multiValueMap, headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, request, String.class);
return response;
}catch(HttpClientErrorException e){
logger.error(e.getResponseBodyAsString());
logger.error(e.getMessage());
return null;
}
}

How to request POST using RestTemplate, authorized using user password

Can some one tell how I can use RestTemplate to POST a HttpEntity object using Authorization. I am using below code in test application
Client Side :
public class FifthWay extends Thread {
public void run() {
String plainCreds = "anuj:khare";
byte[] plainCredsBytes = plainCreds.getBytes();
byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
String base64Creds = new String(base64CredsBytes);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);
HttpEntity<String> postRequest = new HttpEntity<String>("FifthWay",headers);
RestTemplate rt = new RestTemplate();
rt.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
rt.getMessageConverters().add(new StringHttpMessageConverter());
String postUri = new String("http://169.194.48.182:8080/trade-capture-service/deals/persist");
ResponseEntity<String> responseForPost = rt.exchange(postUri,HttpMethod.POST, postRequest, String.class);
String responseStringForPost = responseForPost.getBody();
System.out.println(responseStringForPost);
}
}
Server side :
#Controller
#RequestMapping("/deals")
public class RestController {
...
...
#RequestMapping(value = "/check", method = RequestMethod.GET)
public #ResponseBody
String justACheck() {
System.out.println("It Works");
return "It works";
}
Getting errors like :
Exception in thread "Thread-4" org.springframework.web.client.HttpClientErrorException: 415 Unsupported Media Type
OR
Exception in thread "Thread-4" org.springframework.web.client.HttpClientErrorException: 400 Bad Request
Please help
Here is the example of RestTemplate exchange :
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpHeaders requestHeaders = new HttpHeaders();
final HttpEntity entity = new HttpEntity(restCanvas, requestHeaders);
return restTemplate.exchange(canvasAddUrl + value, HttpMethod.POST, entity, Integer.class);
Here canvasAddURL is the URL you wish to call with context-path. If you want to add a cookie to it, lemme know, i have removed that code as it is most of the time not necessary. The return value for this is ResponseEntity<Integer> . Check it out.

How to do file upload with Spring REST API and Test it with RestTemplate

I want to,
1) Implement some REST service method with Spring Rest API for upload some files from my remote web client.
2) Test that with my RestTemplate based remote web client.
If any one has some idea please help me. Thanks.
Some of my Spring REST API base methods are as below,
#RequestMapping(value="user/create/{userRoleName}", method=RequestMethod.POST)
public #ResponseBody User create(#RequestBody User user, #PathVariable String userRoleName, HttpServletResponse response) { }
Some of my remote client's Spring RestTemplate base codes are as below,
Map<String, String> vars = new HashMap<String, String>();
vars.put("userRoleName", userRoleName);
ResponseEntity<User> REcreateUser = restTemplate.postForEntity(IMC_LAB_SKELETON_URL + "/user/create/{userRoleName}", newUser, User.class, vars);
User createUser = REcreateUser.getBody();
Try this:
public class FileUploadService {
#POST
#Path("/file")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(Attachment attachment,#Context HttpServletRequest request) {
DataHandler handler = attachment.getDataHandler();
try {
InputStream stream = handler.getInputStream();
MultivaluedMap map = attachment.getHeaders();
OutputStream out = new FileOutputStream(new File(getFileName(map)));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
stream.close();
out.flush();
out.close();
} catch(Exception e) {
e.printStackTrace();
}
}
return Response.ok("file uploaded").build();
}
This can easily be tested with RestTemplate.
Imports:
MediaType
Attachment
MultivaluedMap
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> bodyMap = new LinkedMultiValueMap<>();
//Json Payload as String
bodyMap.add("payload", payload);
for (File file : fileArray) {
bodyMap.add("file", new FileSystemResource(file));
}
HttpEntity<MultiValueMap<String, ?>> entity = new
HttpEntity<MultiValueMap<String, ?>>(bodyMap, headers);

Resources