How to send Gzip Request using RestTemplate? - java

I have the following code with large pojo (MyRequest) which I wanted to send in Gzip, but it's unable to hit the end-point that accept Gzip request. Am I creating Gzip request correctly? Do I need to send the pojo as a file?
MyRequest request = new MyRequest ();
HttpHeaders httpHeaders = HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_ENCODING, "gzip");
httpHeaders.add(HttpHeaders.ACCEPT_ENCODING, "gzip");
httpHeaders.add(HttpHeaders.CONTENT_TYPE, "gzip");
HttpEntity<byte[]> entity = new HttpEntity<>(compress(request), headers);
ResponseEntity<MyResponse> response = restTemplate.postForEntity(url, entity, MyResponse.class);
public static Byte[] compress(byte[] body) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) {
gzipOutputStream.write(body);
}
return baos.toByteArray();
}
Kindly share an example of Gzip encoding using RestTemplate, thanks

accept-encoding: gzip, deflate, br
Is for http clients to make requests to a server and expect an http response that is:
content-encoding: gzip
To what you are doing see this answer: https://serverfault.com/a/56707

Related

Problem with spring RestTemplate POST request

Trying to make a post call on one of our servers, but getting 400 BAD_REQUEST all the time
static void postUserToken()
{
final String url = "SERVER ADDRESS";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
MultiValueMap<String, String> requestBody= new LinkedMultiValueMap<>();
requestBody.add("userName", "TESTUSER");
requestBody.add("password", "TESTPASSWORD");
requestBody.add("auth", "secEnterprise");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class );
System.out.println(response);
}
get request to the same address works, post request via Postman works fine with the same body and headers
what am I missing ?
EDIT
calls from postman
POST /api/call/ HTTP/1.1
Host: SEREVERADDRESS:6405
Content-Type: application/json
Accept: application/json
User-Agent: PostmanRuntime/7.15.0
Cache-Control: no-cache
Postman-Token: token1,token2
Host: SEREVERADDRESS:6405
accept-encoding: gzip, deflate
content-length: 92
Connection: keep-alive
cache-control: no-cache
{
"password": "PASSWORD",
"auth": "secEnterprise",
"userName": "USER"
}
in response I get an object like this {"token":"longtoken"}
You are using a MultiValueMap however the json you send from postman looks like a simple Map.
This will produce {"key1":["val1"]} instead of {"key1":"val1"}
The problem might be in the
headers.setContentType(MediaType.APPLICATION_JSON);
Try using headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); or convert your data to a proper JSON.
More on this: https://www.baeldung.com/rest-template (4.4. Submit Form Data)
As far as I understand the problem and since I do not know your rest call details, I provide below the approach you can try.
Remove the following line.
requestBody.add("auth", "secEnterprise");
Add the line
headers.setHeader("auth", "secEnterprise");
If you are using other version of Apache Http Client, you can use the following code snippet.
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("header-name" , "header-value");

Spring Boot multipart content type HTTP Request using RestTemplate

I am trying to emulate this request using RestTemplate in Spring Boot
curl -X POST
'https://my.craftar.net/api/v0/image/?api_key=123456789abcdefghijk123456789abcdefghijk'
-F "item=/api/v0/item/4fe672886ec142f6ab6d72d54acf046f/"
-F "file=#back_cover.png"
Here's my code:
MultiValueMap<String, Object> params= new LinkedMultiValueMap<>();
params.add("item", "/api/v0/item/4fe672886ec142f6ab6d72d54acf046f/");
final String filename=file.getOriginalFilename();
Resource contentsAsResource = new ByteArrayResource(file.getBytes()){
#Override
public String getFilename(){
return filename;
}
};
HttpHeaders imageHeaders = new HttpHeaders();
imageHeaders.setContentType(MediaType.IMAGE_PNG);
HttpEntity<Resource> imageEntity = new HttpEntity<Resource>(contentsAsResource, imageHeaders);
params.add("file", imageEntity);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.ALL));
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String,Object>> requestEntity =new HttpEntity<>(params,headers);
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(url,HttpMethod.POST, requestEntity, String.class);
return responseEntity.getBody();
} catch (final HttpClientErrorException httpClientErrorException) {
return httpClientErrorException.getResponseBodyAsString();
} catch (Exception exception) {
return exception.getMessage();
}
The above request throws a HttpClientErrorException and this what the response body looks like
{"error": {"message": "Expected multipart/form-data; boundary=<..> content but got multipart/form-data;boundary=x6G0xWVxdZX4n8pYNU8ihGAnCg4Twj3DgMARYDs.", "code": "WRONG_CONTENT_TYPE"}}
I have also tried using FileSystemResource, but it throws the same exception. The problem probably lies in formatting the data in multipart content-type.
If it can help, this is the code template generated by Postman on a successful request using Okhttp.
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
RequestBody body = RequestBody.create(mediaType,
"------WebKitFormBoundary7MA4YWxkTrZu0gW\r\n
Content-Disposition: form-data; name=\"item\"\r\n\r\n/api/v0/item/3d8dcdd1daa54bcfafd8d1c6a58249b5/\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\n
Content-Disposition: form-data; name=\"file\"; filename=\"times_logo.png\"\r\nContent-Type: image/png\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--");
Request request = new Request.Builder()
.url("https://my.craftar.net/api/v0/image/?api_key=c6d4750c7368806fab27294fba8d0f93d48e1e11")
.post(body)
.addHeader("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
.addHeader("cache-control", "no-cache")
.addHeader("Postman-Token", "cf09a989-338e-4d68-8968-b30a43384e5f")
.build();
Response response = client.newCall(request).execute();
just add the Resource to params instead of creating a HttpEntity
params.add("file", contentsAsResource);

Prevent Spring's RestTemplate from adding header for each parameters in multipart/form-data

I have to use Spring's RestTemplate to call an external API that takes a POST request with Content-Type: multipart/form-data. The input data are only key-values, no attachments but the server enforce me the use multipart/form-data.
Following is the raw request that works fine.
POST http://the-api:8080 HTTP/1.1
Content-Type: multipart/form-data; boundary=--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Accept: */*
Host: the-api:8080
accept-encoding: gzip, deflate
content-length: 680
Connection: keep-alive
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param1"
value1
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param2"
value2
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL--
Following is the raw request that I extracted and rearranged from the log of the RestTemplate, it did not work because the server mistook the header for the value.
POST http://the-api:8080 HTTP/1.1
Content-Type: multipart/form-data; boundary=--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Accept: */*
Host: the-api:8080
accept-encoding: gzip, deflate
content-length: 680
Connection: keep-alive
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param1"
Content-Type: text/plain;charset=UTF-8
Content-Length: 29
value1
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param2"
Content-Type: text/plain;charset=UTF-8
Content-Length: 14
value2
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL--
Following is the code
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("param1", "value1);
params.add("param2", "value2);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
URI uri = UriComponentsBuilder.fromHttpUrl("http://the-api:8080")
.build().encode(Charset.forName("UTF-8")).toUri();
return restTemplate.postForObject(uri, request, KKPMailResponse.class);
Question
How to prevent Spring's RestTemplate from automatically add the header Content-Type: text/plain;charset=UTF-8 and Content-Length: xx for each parameters
I didn't find a way to prevent Spring from generating the entries, but you can use an interceptor to remove them before sending the request. For that you have to manipulate the request body in the interceptor as follows:
public class MultiPartFormDataCleaningInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
final MediaType contentType = request.getHeaders().getContentType();
if (contentType != null
&& MediaType.MULTIPART_FORM_DATA.getType().equals(contentType.getType())
&& MediaType.MULTIPART_FORM_DATA.getSubtype().equals(contentType.getSubtype())) {
return execution.execute(request, stripContentTypeAndLength(body));
}
return execution.execute(request, body);
}
private byte[] stripContentTypeAndLength(byte[] body) {
final String bodyStr = new String(body);
final StringBuilder builder = new StringBuilder();
try (final Scanner scanner = new Scanner(bodyStr)) {
while (scanner.hasNextLine()) {
final String line = scanner.nextLine();
if (!line.startsWith("Content-Type:")
&& !line.startsWith("Content-Length:")) {
builder.append(line).append("\r\n");
}
}
}
final String newBodyStr = builder.toString();
return newBodyStr.getBytes();
}
}
If think you can use ClientHttpRequestInterceptor to remove headers:
public class SomeHttpRequestInterceptor implements ClientHttpRequestInterceptor
{
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException
{
HttpHeaders headers = request.getHeaders();
headers.remove("your header 1);
headers.remove("your header 2);
return execution.execute(request, body);
}
}
And set it in RestTemplate in this way:
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = Arrays.asList(new CustomHttpRequestInterceptor())
restTemplate.setInterceptors(interceptors);

Special characters are returned as part of download rest service in spring

I have spring based rest web service to download the document. I am facing the strange issue with this service. If i invoke this rest service directly from browser address bar, i am able to download the document with right characters. However if i call the same rest service from the rest client/angular application, i am getting some special characters and document is not in right format.
Below is the spring rest service code
#RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity<byte[]> downloadResume(HttpServletRequest request,
HttpServletResponse response,
#RequestParam(value = "id") String id)
throws Exception {
File file = new File("C:/resume.doc");
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
try {
for (int readNum; (readNum = fis.read(buf)) != -1;) {
bos.write(buf, 0, readNum);
}
} catch (IOException ex) {
}
byte[] bytes = bos.toByteArray();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
httpHeaders.setContentLength(bytes.length);
httpHeaders.setContentDispositionFormData("attachment", "test");
return new ResponseEntity(bytes, httpHeaders, HttpStatus.OK);
}
Below are the request header parameters used while calling the download service from the rest client.
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:8080/dist/index.html
Cookie: _ga=GA1.1.1133600505.1423631305; _gat=1
Connection: keep-alive
Update:
Added the angular code below. But the same issue exists with the service call from rest client as well.
downloadFactory.download(id)
.then(function(response) {
var a = document.createElement("a");
document.body.appendChild(a);
var blob = new Blob([response.data], {type: "application/octet-stream"});
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = 'test';;
a.click();
window.URL.revokeObjectURL(url);
}, function(error) {
});

Spring - changing Content-type HTTP header

I am communicating with a JSON API using Robospice. This is how I set the headers for a POST request:
mHttpHeaders = new HttpHeaders();
mHttpHeaders.add("Content-Type", "application/json");
Log.d(Global.TAG, mHttpHeaders.toString());
The problem is that I get a header which looks like that: {Content-Type=[application/json]} where I need to send Content-Type:application/json (which seems to be the only header that my server accepts). I can't find any way to change it (tried with both add() and setContentType() methods), how do I do that?
You should try followings and it works for me. I'm using Spring rest template inside Robospice.
MediaType mediaType = new MediaType("application", "json");
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(mediaType);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
The output
{Content-Type=[application/json]}
you see in the Log statement is just the String returned by the toString() method, it's not the actual header sent. For example
public static void main(String[] args) throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
System.out.println(headers);
}
prints
{Content-Type=[application/json]}
If you are using the HttpClient and HttpRequestBase (HttpGet, HttpPost, etc.) correctly, the header will be sent just fine.

Categories