Spring RestTemplate Handling Multiple Responses - spring

I'm consuming a restful service, What I observed is based on the request I see multiple responses for the same end point.
For Ex : I request a GET call with the some parameters for a PDF document.
if the response is good and the content type is application/pdf its giving a pdf document.
if the document is not available, the content type is application/xml and the response is giving the error code, error description.
Any input is much appreciated !
Thanks,
Sudheer.

You can use the ResponseEntity class of Spring, you can set the class to return the object what you want. You can change the content type, and everything you want.
here there is an example of file
#RequestMapping(value = URIConstansts.GET_FILE, produces = { "application/json" }, method = RequestMethod.GET)
public #ResponseBody ResponseEntity getFile(#RequestParam(value="fileName", required=false) String fileName,HttpServletRequest request) throws IOException{
ResponseEntity respEntity = null;
byte[] reportBytes = null;
File result=new File("/home/arpit/Documents/PCAP/dummyPath/"+fileName);
if(result.exists()){
InputStream inputStream = new FileInputStream("/home/arpit/Documents/PCAP/dummyPath/"+fileName);
String type=result.toURL().openConnection().guessContentTypeFromName(fileName);
byte[]out=org.apache.commons.io.IOUtils.toByteArray(inputStream);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("content-disposition", "attachment; filename=" + fileName);
responseHeaders.add("Content-Type",type);
respEntity = new ResponseEntity(out, responseHeaders,HttpStatus.OK);
}else{
respEntity = new ResponseEntity ("File Not Found", HttpStatus.OK);
}
return respEntity;
}
Here there is an example of Json
#ResponseBody ResponseEntity<? extends AbstractResponse> createUser(#RequestBody String requestBody) {
if(!valid(requestBody) {
ErrorResponse errResponse = new ErrorResponse();
//populate with error information
return new ResponseEntity<ErrorResponse> (errResponse, HTTPStatus.BAD_REQUEST);
}
createUser();
CreateUserSuccessResponse successResponse = new CreateUserSuccessResponse();
// populate with more info
return new ResponseEntity<CreateUserSuccessResponse> (successResponse, HTTPSatus.OK);
}

Related

Problem while uploading MultiValueMap using FeignClient for MultipartFile receiver API

I am facing issues while sending MultipartFile from one service to another service.
API which I want to call is,
#PostMapping(value = "/posts/{postId}/comments/{commentId}/attachments")
#JsonView(CaseJsonView.ClientWithComments.class)
public ResponseEntity<?> createCommentAttachment(#PathVariable final String postId, #PathVariable final String commentId, #RequestParam("payload") final MultipartFile multipartFile, #RequestParam final String filename, final HttpServletRequest request) throws JsonProcessingException {
try {
FileUtils.writeByteArrayToFile(new File("C:\\CODE\\data\\" + filename), multipartFile.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
Here empty file is getting created when I call this API like below,
FeignClient
#FeignClient(name = "post-service")
public interface CommentClient {
#RequestMapping(method = RequestMethod.POST, value = "/posts/{postId}/comments/{id}/attachments?filename={filename}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
void storeCommentAttachmentPayload(#PathVariable("postId") String postId, #PathVariable("id") String id, #RequestBody MultiValueMap<String, Object> map, #PathVariable("filename") String filename);
}
And I am using this FeignClient like below,
public void sendAttachment() {
//Adding only attachment code.
// Here attachmentClient is some other FeignClient which is returning attachment.
final Resource resource = attachmentClient.getAttachmentPayloadById(attachementId);
final MultipartFile multipartFile = new InMemoryMultipartFile(filename, filename,
mimeType, IOUtils.toByteArray(resource.getInputStream()));
final MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
final ByteArrayResource byteArrayResource = new
ByteArrayResource(multipartFile.getBytes()) {
#Override
public String getFilename() {
return filename == null ? "" : filename;
}
};
map.add(PAYLOAD, byteArrayResource);
commentService.storeCommentAttachmentPayload(postId commentId, map, filename);
}
Observation:
Here my observation is that when I save files on disk from this method, all data is shown properly.
But at the receiver end empty file is saved.
Another observation is that somehow byte array size is less at the receiver end than the sender end. Check the below image,
One more observation is that text files are uploaded properly.
So finally I found a solution to my problem. Instead of uploading a file using FeignClient, I am uploading using RestTemplate as below,
final List<ServiceInstance> postServiceInstances = discoveryClient.getInstances("post-service");
if (postServiceInstances != null && !postServiceInstances.isEmpty()) {
final HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<MultiValueMap<String, Object>>(multiValuMap);
final ResponseEntity<String> response = restTemplate
.postForEntity(postServiceInstances.get(0).getUri() + "/posts/" + postId + "/comments/" + commentId + "/attachments?filename=" + filename, entity, String.class);
if (response.getStatusCode() != HttpStatus.CREATED) {
throw new Exception("Exception from post-service: " + response.getStatusCode());
}
} else {
throw new Exception("No post-service instance found");
}
Not actually a perfect solution but it is solving my purpose.
Also, I have added RestTemplate Interceptor which adds a token in my request.
Still, the solution using FeignClient will be appreciated.

RestTemplate.exchange doesn't work properly. Missing params thrown

I've got 2 aplication one calling another. In the first one I have
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>();
parameters.add("stationFrom", stationfrom);
parameters.add("stationTo", stationto);
parameters.add("operator", 5);
parameters.add("dateTimeFrom", sdfnative.format(sdfeskm.parse(departuredate)));
parameters.add("dateTimeTo", sdfnative.format(sdfeskm.parse(departuredate).getTime() + 60*60*1000));
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(parameters, headers);
jsonTrain = restTemplate.exchange(url, HttpMethod.GET, request, String.class).toString();
the url is defined as global http://ip:8090/connection/search
When I'm looking at the request I can see that every parameter has it's proper value, but in the restTemplate.exchange line I got error "Parameter stationFrom is missing"
My endpoint on provided IP looks like :
#GetMapping(value = "/connection/search")
public ResponseEntity<String> getConnection(#RequestParam(value = "stationFrom") int stationFrom,
#RequestParam(value = "stationTo") int stationTo,
#RequestParam(value = "operator") int operator,
#RequestParam(value = "dateTimeFrom") String dateTimeFrom,
#RequestParam(value = "dateTimeTo") String dateTimeTo) throws JSONException
I don't know why it throws me this error if stationFrom has for example value 40 and I can see it in debug/logs
Of course calling provided endpoint from postman, with the same data gives me expected result :)
You are passing the query parameters (RequestParams) as headers.
Check the following example how to pass RequestParams:
public void findUserById()
{
String username = "chathuranga";
String password = "123";
Integer userId = 1;
String url = "http://localhost:" + port + "/users/" + userId;
//setting up the HTTP Basic Authentication header value
String authorizationHeader = "Basic " + DatatypeConverter.printBase64Binary((username + ":" + password).getBytes());
HttpHeaders requestHeaders = new HttpHeaders();
//set up HTTP Basic Authentication Header
requestHeaders.add("Authorization", authorizationHeader);
requestHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
//request entity is created with request headers
HttpEntity<AddUserRequest> requestEntity = new HttpEntity<>(requestHeaders);
//adding the query params to the URL
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("name", "chathuranga")
.queryParam("email", "chathuranga.t#gmail.com");
ResponseEntity<FindUserResponse> responseEntity = restTemplate.exchange(
uriBuilder.toUriString(),
HttpMethod.GET,
requestEntity,
FindUserResponse.class
);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
System.out.println("response received");
System.out.println(responseEntity.getBody());
} else {
System.out.println("error occurred");
System.out.println(responseEntity.getStatusCode());
}
}

Get specific response value from the API in SpringBoot

I am calling one post call and getting the response like below :
Now I want to get the value of the access token and do some logic. How to get the value from the response? Can anyone please help.
Service code of the API.
public String getToken(User user) throws JsonMappingException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(clientId+clientPass);
String plainCreds = clientId+":"+clientPass;
byte[] plainCredsBytes = plainCreds.getBytes();
byte[] base64CredsBytes = Base64.getEncoder().encode(plainCredsBytes);
String base64Creds = new String(base64CredsBytes);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);
HttpEntity<String> request=new HttpEntity<String>(headers);
String uri = url+ user.getUser_id()
+ "&password=" + user.getPassword();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.POST, request, String.class) ;
JsonNode newNode = mapper.readTree(result.getBody());
ObjectNode node = ((ObjectNode) newNode).put("Authentication", "Successful");
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
}
I was able to figure it out.
I used
node.get("access_token")
And it worked!
You can write DTO for response:
public class ResponseSample {
#JsonProperty("access_token")
String accessToken;
....
}
(or deserialize to Map)
and instead calling ResponseEntity<String> result = restTemplate.exchange(...) you can call
ResponseEntity<ResponseSample> result = restTemplate.exchange(..., ResponseSample.class)

Send the response as key value pair from Spring controller?

In our application we are getting the request object from third party as form data. We are processing the form data in Spring controller and send the response back. In spring controller we have wrote the logic like below.
#RequestMapping(value = "/oci/html/setup", method = RequestMethod.POST,
produces = {MediaType.TEXT_HTML_VALUE }, consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE })
#ResponseStatus(value= HttpStatus.OK)
#ResponseBody
public String handleOciSetUpRequest1(OciSetupRequest reqObject)
{
if (LOG.isDebugEnabled())
{
LOG.debug("Oci Setup Request Object: " + reqObject.toString());
}
final OciSetupResponse response = getOciService().processOciSetUpRequest(reqObject);
return response.toString();
}
Request Object:
identity: 1234
sharedSecret: password
Expected Response object:
SessionId=1236547878
URL=https://sample.com
Here we need to send the response in the form of key value pair html response. Anyone can help on this
How to send the html response as key-value pair from Spring controller.
If sample code provided will be appreciated....
Thanks in advance
I see that you try to return in a response body, the best would be by means of a rest controller, and also add exception handling, with what you would have:
#RestController
public class SomeClassController {
#PostMapping("/oci/html/setup")
public ResponseEntity<?> reportRecords(OciSetupRequest reqObject) {
Map<String, Object> response = new HashMap<>();
try {
if (LOG.isDebugEnabled())
{
LOG.debug("Oci Setup Request Object: " + reqObject.toString());
}
final OciSetupResponse response = getOciService().processOciSetUpRequest(reqObject);
response.put("SessionId", "1236547878");
response.put("URL", "https://sample.com");
return new ResponseEntity<>(response, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
response.put("message", e.getMessage());
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Use a map:
#ResponseBody
public Map<String, String> handleOciSetUpRequest1(OciSetupRequest reqObject)
{
if (LOG.isDebugEnabled())
{
LOG.debug("Oci Setup Request Object: " + reqObject.toString());
}
final OciSetupResponse response = getOciService().processOciSetUpRequest(reqObject);
Map<String, String> responseMap - new HashMap();
map.put("SessionId", "someValue");
map.put("URL", "someValue");
return responseMap;
//returns JSON {"SessionId":"someValue", "URL":"someValue"}
}

#RequestPart with mixed multipart request, Spring MVC 3.2

I'm developing a RESTful service based on Spring 3.2. I'm facing a problem with a controller handling mixed multipart HTTP request, with a Second part with XMLor JSON formatted data and a second part with a Image file .
I am using #RequestPart annotation for receiving the request
#RequestMapping(value = "/User/Image", method = RequestMethod.POST, consumes = {"multipart/mixed"},produces="applcation/json")
public
ResponseEntity<List<Map<String, String>>> createUser(
#RequestPart("file") MultipartFile file, #RequestPart(required=false) User user) {
System.out.println("file" + file);
System.out.println("user " + user);
System.out.println("received file with original filename: "
+ file.getOriginalFilename());
// List<MultipartFile> files = uploadForm.getFiles();
List<Map<String, String>> response = new ArrayList<Map<String, String>>();
Map<String, String> responseMap = new HashMap<String, String>();
List<String> fileNames = new ArrayList<String>();
if (null != file) {
// for (MultipartFile multipartFile : files) {
String fileName = file.getOriginalFilename();
fileNames.add(fileName);
try {
file.transferTo(new File("C:/" + file.getOriginalFilename()));
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
responseMap.put("displayText", file.getOriginalFilename());
responseMap.put("fileSize", "" + file.getSize());
response.add(responseMap);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Accept", "application/json");
return new ResponseEntity<List<Map<String, String>>>(response,
httpHeaders, HttpStatus.OK);
}
User.java will be like this-
#XmlRootElement(name = "User")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int userId;
private String name;
private String email;
private String company;
private String gender;
//getter setter of the data members
}
To my understanding, using the #RequestPart annotation I would expect the XML multipart section to be evaluated depending on its Content-Type and finally un-marshalled into my User class (I'm using Jaxb2, the marshaller/unmarhaller is properly configured in the application context and the procedure is working fine for all the other controller methods when I pass the XML data as body and use the #RequestBody annotation).
But what is actually happening is that, although the file is correctly found and parsed as MultipartFile, the "user" part is never seen and the request is always failing, not matching the controller method signature.
I reproduced the problem with several clients type and I am confident the format of the multipart request is ok.
Please help me to solve this issue, Maybe some workaround will be there to receive mixed/multipart request.
Thanks and Regards,
Raghvendra
I have managed to solve the problem
Endpoint example:
#PostMapping("/")
public Document create(#RequestPart Document document,
#RequestPart(required = false) MultipartFile file) {
log.debug("#create: document({}), file({})", delegation, file);
//custom logic
return document;
}
Exception:
"error_message": "Content type 'application/octet-stream' not supported"
Exception is thrown from the next method:
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(HttpInputMessage,MethodParameter,Type)
Solution:
We have to create custom converter #Component, which implements HttpMessageConverter or HttpMessageConverter and knows about MediaType.APPLICATION_OCTET_STREAM. For simple workaround it's enough to extend AbstractJackson2HttpMessageConverter
#Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
/**
* Converter for support http request with header Content-Type: multipart/form-data
*/
public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
}
#Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}
#Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return false;
}
#Override
protected boolean canWrite(MediaType mediaType) {
return false;
}
}
Not sure if you had fixed your problem, but I also had a similar problem where my JSON object was not getting picked up by my controller when mixing #RequestPart and MultipartFile together.
The method signature for your call looks correct:
public ResponseEntity<List<Map<String, String>>> createUser(
#RequestPart("file") MultipartFile file, #RequestPart(required=false) User user) {
// ... CODE ...
}
However make sure your request looks something like this:
POST /createUser
Content-Type: multipart/mixed; boundary=B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
--B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
Content-Disposition: form-data; name="user";
Content-Type: application/xml; charset=UTF-8
<user><!-- your user xml --></user>
--B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
Content-Disposition: form-data; name="file"; filename="A551A700-46D4-470A-86E7-52AD2B445847.dat"
Content-Type: application/octet-stream
/// FILE DATA
--B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E--
You can use #RequestPart from
org.springframework.web.bind.annotation.RequestPart;
It is used as Combining #RequestBody and file upload.
Using #RequestParam like this
#RequestParam("file") MultipartFile file
you can upload only file and multiple single data (key value )
like
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public void saveFile(
#RequestParam("userid") String userid,
#RequestParam("file") MultipartFile file) {
}
you can post JSON Object data and and File both using #RequestPart like
#RequestMapping(value = "/patientp", method = RequestMethod.POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> insertPatientInfo(
#RequestPart PatientInfoDTO patientInfoDTO,
#RequestPart("file") MultipartFile file) {
}
You are not limited to using multipart file uploads directly as controller method parameters. Your form objects can contain Part or MultipartFile fields, and Spring knows automatically that it must obtain the values from file parts and converts the values appropriately.
Above method can respond to the previously demonstrated multipart request containing a single file. This works because Spring has a built-in HTTP message converter that recognizes file parts. In addition to the javax.servlet.http.Part type, you can also convert file uploads to org.springframework.web.multipart.MultipartFile. If the file field permits multiple file uploads, as demonstrated in the second multipart request, simply use an array or Collection of Parts or MultipartFiles.
#RequestMapping(value = "/patientp", method = RequestMethod.POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> insertPatientInfo(
#RequestPart PatientInfoDTO patientInfoDTO,
#RequestPart("files") List<MultipartFile> files) {
}
Happy To Help...
I have managed to solve problem:
#SuppressWarnings("rawtypes")
#RequestMapping(value = "/DataTransfer", method = RequestMethod.POST, produces = {
MediaType.APPLICATION_JSON_UTF8_VALUE }, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE} )
#ApiOperation(value = "Sbm Data Transfer Service", response = Iterable.class)
#ApiResponses(value = { #ApiResponse(code = 200, message = "Successfully find."),
#ApiResponse(code = 400, message = "There has been an error."),
#ApiResponse(code = 401, message = "You are not authorized to save the resource"),
#ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found") })
ResponseEntity processDataTransfer(#RequestPart(name="file") MultipartFile file, #RequestPart(name="param") DataTransferInputDto param);
have you tried
ResponseEntity<List<Map<String, String>>> createUser(
#RequestPart("file") MultipartFile file, #RequestBody(required=false) User user) {
or
ResponseEntity<List<Map<String, String>>> createUser(
#RequestPart("file") MultipartFile file, #RequestParam(required=false) User user) {
If this does not work can you show us mapping.xml

Resources