415 Error when authenticating - Spring boot and RestTemplate - spring-boot

I am working on a third-party REST API and I am going to authenticate with their API by making a POST request to the provided end point. I tested this end point using Postman and it works fine, and now I wanted to do it programmatically.
I am using Spring boot 3.0 for development and RestTemplate as the http client. The code I have implemented is below, it gives me a 415 UNSUPPORTED_MEDIA_TYPE error.
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==");
headers.add("Accept", "application/json");
headers.add("Content-Type","application/x-www-form-urlencoded");
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<String, String>();
requestBody.add("grant_type", "client_credentials");
HttpEntity formEntity = new HttpEntity<MultiValueMap<String, String>>(requestBody, headers);
String serverEndPoint = "https://xxxxxxx.xxx/auth/oauth2/xx/token";
ResponseEntity<String> response = restTemplate.exchange(serverEndPoint, HttpMethod.POST,formEntity, String.class);
System.out.println(response.getStatusCode().value());
Build RestTemplate
#Bean
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
// don't throw
}
});
return restTemplate;
}
But same logic I have implemented using Jersey http client as mentioned below, it gives me successful response with 200 response code.
JacksonJsonProvider jacksonJsonProvider = new JacksonJaxbJsonProvider()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Client client = ClientBuilder.newClient(new ClientConfig(jacksonJsonProvider));
Form form = new Form();
form.param("grant_type", "client_credentials");
String serverEndPoint = "https://xxxxxxx.xxx/auth/oauth2/xx/token";
Response response = client.target(serverEndPoint)
.request("application/json")
.header("Authorization", "Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==")
.post(Entity.entity(form, APPLICATION_FORM_URLENCODED));
System.out.println(response.getStatus());
Kindly help me to resolve this since I need to move forward with the RestTemplate client.
cUrl From Postman
curl --location --request POST 'https://xxxxxxxxxxxxx.xxx/auth/oauth2/xx/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==' \
--header 'Cookie: coolie data bla bla' \
--data-urlencode 'grant_type=client_credentials'

If I inspect what the successful calls (curl and the Jersey HttpClient) send to the server vs what RestTemplate.exchange() sends, the only significant difference is that RestTempate.exchange() appends the charset=utf-8 to the Content-Type header.
I suspect that the sever you are calling is too strict on Content-Type and throws a 415 if it does not match "application/x-www-form-urlencoded" exactly.
To prevent this you need to use RestTempate.postForObject() instead. Try changing your RestTemplate call code as follows:
String serverEndPoint = "https://httpbin.org/post";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> entity = new HttpEntity<>("grant_type=client_credentials", headers);
String result = restTemplate.postForObject( serverEndPoint, entity, String.class);
The MediaType is org.springframework.http.MediaType.
Note that I call httpbin.org in the code fragment. I find it extremely useful in researching an issue like this.

Related

How to add file as Form data in Http request in spring boot

I'm writing test cases to a controller in spring boot which takes MultipartFile as RequestParam. In the test case method I'm using TestRestTemplate.exchange() to send the request to the controller. I'm not sure how to make the Headers correctly so that I can send the request.
The Postman curl looks like this:
curl --location --request POST 'localhost:9091/response/upload'
--form 'file=#"/home/adityak/Downloads/ClientLog_NEW.txt"'
For file uploading to any service or endpoint
private String testExchange(File file) {
//add file
LinkedMultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
params.add("file", new FileSystemResource(file));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(
"/upload-file",
HttpMethod.POST,
requestEntity,
String.class);
HttpStatus statusCode = responseEntity.getStatusCode();
if (statusCode == HttpStatus.ACCEPTED) {
result = responseEntity.getBody();
}
return result;
}

How to read response with Content-Type text/plain;charset=UTF-8 using RestTemplate

I have an API (/get-sas-token) that is returning a response with Content-Type=text/plain;charset=UTF-8.
In Postman when I hit this API it returns as sig=PAd%2By7yzue0G%2FVeMKbwvR%2F%2B5a3X8CUTablCIhS3uCuk%3D&s
The code for this API is
#GetMapping("/get-sas-token/{containerName}")
public String getSASToken(#PathVariable("containerName") String containerName)
throws InvalidKeyException, URISyntaxException, StorageException {
CloudBlobContainer appcontainer = config.blobClient().getContainerReference(containerName);
return appConfiguration.generateSASToken(appcontainer);
}
My other microservice is trying to call this RestAPI using RestTemplate via a get request. The response I'm getting in Postman has instead of special characters like �������{J�J��t�\b�`$ؐ#������iG# some weird stuff. So I guess somethings wrong with the character encoding.
P.S : If I remove the Accept-Encoding(gzip, deflate, br) header from Postman it works.How can i get it working in my code
ResponseEntity<String> imageUrl = restTemplate.exchange(
fileServiceUrl + "/get-sas-token/" + containerReference, HttpMethod.GET, request, String.class);
logger.info("imageUrl body-------->>" + imageUrl.getBody());
It prints
imageUrl body-------->>?`I?%&/m?{J..................
I have tried all the possible soultions but nothing works for me
tried this::
template.getMessageConverters()
.add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
ResponseEntity<Object> response = template.exchange(endpoint, method, entity,
Object.class);
Also this::
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Accept", "application/json");

POST request with form data using Spring RestTemplate

I have to make a HTTP POST request with form data to an external API. Currently, I am invoking the API using the below curl command
curl https://books.zoho.com/api/v3/contacts?organization_id=10234695
-X POST
-H "Authorization: Zoho-oauthtoken 216793f385b9dd6e125f"
-H "Content-Type: application/x-www-form-urlencoded;charset=UTF-8"
-F 'JSONString="{
"contact_name": "Bowman and Co",
"company_name": "Bowman and Co",
"website": "www.bowmanfurniture.com",
"contact_type": "customer"
}
I have implemented ClientHttpRequestInterceptor interface and registered the interceptor with the RestTemplate. I am not sure, how to post the HTTP request with form data. Kindly let me know, how to submit the form data or point me to any reference.
Inside my Controller class:
#RestController
#RequestMapping("/api/v1/contacts")
public class ContactController {
#Autowired
private RestTemplate restTemplate;
#PostMapping("/")
public Invoice createContact(#RequestBody Contact contact){
//TODO
return null;
}
}
You could use a rest template to make a post request with form data.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("data", "data");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity( url, request , String.class );
Spring will automatically take care of the message converters used in this case.(The message converter used will be FormHttpMessageConverter).
Read Doc

curl ok, RestTemplate fails

This works:
curl --verbose -H "Content-Type: application/json" --data "{\"parm1\":\"1\", \"parm2\":\"1\"}" http://server:8080/service/meth
and returns:
{"success":true,"errorMessage":null}
This fails:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("parm1", "1");
map.add("parm2", "1");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
String response = (String) restTemplate.postForObject("http://server:8080/service/meth",
request,
String.class);
with 500 Internal Server Error
I'm using springframework-boot 1.3.3.RELEASE and java 1.8 and am at a loss as to what's wrong. The server side log shows no errors other than 500
Has anyone been faced with a Spring RestTemplate call that mysteriously failed? How did you approach diagnosing the problem?

HTTP POST using JSON in Spring Rest

I would like to make a simple HTTP POST using Spring RestTemplate.
the Wesb service accept JSON in parameter for example: {"name":"mame","email":"email#gmail.com"}
public static void main(String[] args) {
final String uri = "url";
RestTemplate restTemplate = new RestTemplate();
// Add the Jackson message converter
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// create request body
String input = "{ \"name\": \"name\", \"email\": \"email#gmail.com\" }";
JsonObject request = new JsonObject();
request.addProperty("model", input);
// set headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Basic " + "xxxxxxxxxxxx");
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);
// send request and parse result
ResponseEntity<String> response = restTemplate
.exchange(uri, HttpMethod.POST, entity, String.class);
System.out.println(response);
}
When I test this code I got this error:
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 400 Bad Request
when I call webservice with Curl I have correct result:
curl -X POST -H "Authorization: Basic xxxxxxxxxx" --header "Content-Type: application/json" --header "Accept: application/json" -d "{ \"name\": \"name\", \"email\": \"email#gmail.com\" } " "url"
try to remove model from the code, as i can see in your curl request you didn't use model attribute and everything works. try this:
public static void main(String[] args) {
final String uri = "url";
RestTemplate restTemplate = new RestTemplate();
// Add the Jackson message converter
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// create request body
String input = "{\"name\":\"name\",\"email\":\"email#gmail.com\"}";
// set headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Basic " + "xxxxxxxxxxxx");
HttpEntity<String> entity = new HttpEntity<String>(input, headers);
// send request and parse result
ResponseEntity<String> response = restTemplate
.exchange(uri, HttpMethod.POST, entity, String.class);
System.out.println(response);
}

Resources