How to pass multipart request parameter in rest template spring boot? - spring-boot

I have following controller code in one microservice :
#PostMapping("/posts/{postId}/images")
#RolesAllowed({Roles.USER, Roles.ADMIN})
public ResponseEntity<UploadImageResponse> uploadFile(#RequestParam("image") MultipartFile file, #AuthenticationPrincipal String username, #PathVariable(name = "postId") String postId) {
ImageMetadataEntity metadata = imageService.upload(file, username, postId);
UploadImageResponse uploadImageResponse = new UploadImageResponse(metadata.getFilename(), metadata.getUri(), metadata.getFileType(), metadata.getPostId());
return new ResponseEntity<>(uploadImageResponse, HttpStatus.CREATED);
}
I am calling this API from other microservice using rest template like below:
#Override
public UploadImageResponse uploadFile(UploadImageRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.add(HttpHeaders.AUTHORIZATION, Constants.BEARER + " " + TokenContext.get());
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("image", request.getFile().getBytes());
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
ResponseEntity<UploadImageResponse> response = restTemplate
.postForEntity(String.format(IMAGE_UPLOAD_URL, MEDIA_SERVICE_HOST, request.getPostId()), requestEntity, UploadImageResponse.class);
return response.getBody();
}
But somehow this is not working. I am getting the below error :
2022-11-27 18:52:56.829 WARN 11120 --- [nio-8000-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'image' is not present]
But in when debugged HttpServletRequest.multipartParameterNames has the 'image' field i am sending.
Can someone tell me what is wrong ?

The issue is that the file is sent in request as request.getFile().getBytes(). When request comes to controller, spring checks for the file name and if its present then its added in multiPartFiles else they will be added in multiPartParameterNames in HttpServletRequest. When sent as getBytes(), the file name is null. I have fixed it by adding the below class
public class MultipartInputStreamFileResource extends InputStreamResource {
private final String filename;
public MultipartInputStreamFileResource(InputStream inputStream, String filename) {
super(inputStream);
this.filename = filename;
}
#Override
public String getFilename() {
return this.filename;
}
#Override
public long contentLength() throws IOException {
return -1;
}
}
and the request now is changed as this :
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("image", new MultipartInputStreamFileResource(request.getFile().getInputStream(), request.getFile().getOriginalFilename()));

Related

How to get token from a REST service with Spring

The service provider supplies me with the header data: grant_type, Content-Type. And the body data: grant_type, username and password. Which I use in Postman where it generates OK token. But in the Spring application it generates an error HttpClientErrorException $ BadRequest: 400 Bad Request.
I have the class to set the body data:
public class BodyToken {
private String grant_type = "password";//set body data
private String username = "User";//set body data
private String password = "123";//set body data
private String access_token;
#JsonGetter("access_token")
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
#JsonGetter("grant_type")
public String getGrant_type() {
return grant_type;
}
#JsonGetter("username")
public String getUsername() {
return username;
}
#JsonGetter("password")
public String getPassword() {
return password;
}
}
This is the controller where the header data is set:
#PostMapping("/TokenGeneration")
#ResponseBody
public BodyToken TokenGeneration() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("grant_type", "password");//set header data
headers.set("Content-Type", "application/x-www-form-urlencoded");//set header data
HttpEntity request = new HttpEntity(headers);
headers.add("User-Agent", "Spring's RestTemplate" );
ResponseEntity<BodyToken> response = restTemplate.exchange(
"https://sw/token",
HttpMethod.POST,
request,
BodyToken.class
);
try {
return response.getBody();
} catch (Exception e) {
BodyToken body = new BodyToken();
log.info(e.getMessage());
return body;
}
}
OK was solved with using the Class MultiValueMap and LinkedMultiValueMap. The credentials are added to this new object and it is sent together with the request:
#PostMapping("/TokenGeneration")
#ResponseBody
public BodyToken TokenGeneration() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("grant_type", "password");//set header data
headers.set("Content-Type", "application/x-www-form-urlencoded");//set header data
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();//line solution
body.add("grant_type", "password");//line solution
body.add("username", "user");//line solution
body.add("password", "123");//line solution
HttpEntity request = new HttpEntity(body, headers);//and I add this body to HttpEntity
headers.add("User-Agent", "Spring's RestTemplate" );
ResponseEntity<BodyToken> response = restTemplate.exchange(
"https://sw/token",
HttpMethod.POST,
request,
BodyToken.class
);
try {
return response.getBody();
} catch (Exception e) {
BodyToken body = new BodyToken();
log.info(e.getMessage());
return body;
}
}

How can I send a header with void response?

I'm working on a Spring project.
I'm currently doing this.
public ResponseEntity<?> create(#RequestBody final Some entity) {
// persist the entity here
final URI location = uriComponentsBuilder.path("{id}").buildAndExpand(entity.getId()).toUri();
return ResponseEntity.created(location).build();
}
And I found the #ResponseStatus.
#ResponseStatus(HttpStatus.CREATED)
public void create(#RequestBody #NotNull final BaseType entity) {
// persist the entity here
// Location header to where?
}
Is there any way to send Location header with this way?
You can return response entity like below:
return new ResponseEntity<>(location, HttpStatus.CREATED);
or
HttpHeaders headers = new HttpHeaders();
headers.add(location);
return new ResponseEntity<>(headers, HttpStatus.CREATED);
Try this. It returns your preferred header and status, without body.
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT)
#RequestMapping("/teapot")
public HttpHeaders dummyMethod() {
HttpHeaders h = new HttpHeaders();
h.add("MyHeader", "MyValue");
return h;
}

Spring RestTemplate, getting junk response when http status code is 404

I am writing a rest proxy (it exposes the API and delegates call to other server) and it works fine for the normal case and also for 500 http status code, we get the response from the rest client.
But when we get 404 status code, the Rest API server returns the message but we get junk values from the RestTemplate. We need to pass the same response to other API user but cannot get the same response.
Message returned from REST API Server:
{
"status_code":"0",
"error":{
"code":"404",
"description":"Source not found"
}
}
Getting the below response by RestTemplate client:
Not able to paste the content, attaching the screen shot of the response.
Please see the code below.
#RequestMapping(value = "/api/**")
public #ResponseBody String apiProxy(#RequestBody String body, HttpMethod method, HttpServletRequest request,
HttpServletResponse response) throws URISyntaxException {
RestTemplate restTemplate = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
restTemplate.setInterceptors(Collections.singletonList(new RestClientLoggingInterceptor()));
restTemplate.setErrorHandler(new CustomResponseErrorHandler());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
HttpHeaders httpHeaders = new HttpHeaders();
Enumeration<String> headers = request.getHeaderNames();
String headerName = null;
String headerValue = null;
while (headers.hasMoreElements()) {
headerName = headers.nextElement();
headerValue = request.getHeader(headerName);
httpHeaders.set(headerName, headerValue);
}
HttpEntity<String> httpEntity = new HttpEntity<String>(body, httpHeaders);
URI uri = new URI(ServerProtocol, null, ServerDomain, Integer.valueOf(ServerPort),
request.getRequestURI(), request.getQueryString(), null);
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(uri, method, httpEntity, String.class);
} catch (RestClientResponseException e) {
response.setStatus(e.getRawStatusCode());
return e.getResponseBodyAsString();
}
response.setStatus(responseEntity.getStatusCode().value());
return responseEntity.getBody();
}
ResponseErrorHandler Class
public class CustomResponseErrorHandler extends DefaultResponseErrorHandler {
private static final Logger logger = LogManager.getLogger(CustomResponseErrorHandler.class);
#Override
public void handleError(ClientHttpResponse response) throws IOException {
logger.error("Response error: {} {}", response.getStatusCode(), response.getStatusText());
}
}
RestClientLoggingInterceptor Class
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
ClientHttpResponse response = execution.execute(request, body);
logger.debug("request method:" + request.getMethod());
logger.debug("request URI:" + request.getURI());
logger.debug("request headers:" + request.getHeaders());
logger.debug("request body:" + new String(body, Charset.forName("UTF-8")));
logger.debug("response status code:" + response.getStatusCode());
logger.debug("response headers:" + response.getHeaders());
logger.debug("response body:" + IOUtils.toString(response.getBody(), "UTF-8"));
return response;
}
Thanks
Cannot parse gzip encoded response with RestTemplate from Spring-Web
This was helpful to me for this same issue. You can try this out.

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