I am trying to get push notifications from a resource on Google Drive to my server. I have been looking at this example:
https://developers.google.com/drive/v3/web/push
And I have tried translating that to Java into something like this:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("id", "36d00d08-000d-4723-91bc-a1a6ec302e59");
map.add("type", "web_hook");
map.add("address", "https://mydomain.appspot.com/rest/drive");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity(uri, request, String.class);
I have previously been using Googles libs for Drive to access files. In those cases I didn't need to create the request in such a "manual" way. I have used the class GoogleAuthorizationCodeFlow with a token to authorize my requests. I'm not sure how I should do that with RestTemplate. I am guessing that I need to do something like:
headers.set("Authorization", X);
What should X be here? Is that even the right way to approach authorization?
Edit:
Here is my attempt by reading a secret. The result is HTTP 401:
#Override
public String startListening() throws IOException {
final String fileId = "omitted";
String uri = "https://www.googleapis.com/drive/v3/files/" + fileId + "/watch";
HttpHeaders headers = getHeaders(getSecret());
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(getProperties(), headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(uri, request, String.class);
return response.getStatusCode() + " " + response.getBody() + " " + response.getHeaders();
}
private static HttpHeaders getHeaders(String theString) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + theString);
return headers;
}
private static MultiValueMap<String, String> getProperties() {
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("id", "some uid");
map.add("type", "web_hook");
map.add("address", "https://mydomain.appspot.com/rest/drive");
return map;
}
private static String getSecret() throws IOException {
InputStream in =
ConcreteDriveListenerFactory.class.getResourceAsStream("/drive_secret.json");
StringWriter writer = new StringWriter();
IOUtils.copy(in, writer, "UTF-8");
return writer.toString();
}
As #DalmTo has mentioned, X is for token. With regard to sample POST request for Drive API try this code snippet from this SO thread. It also uses a POST method.
public static void main(String argv[]) throws Exception {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost post = new HttpPost(
"https://www.googleapis.com/drive/v2/files");
post.addHeader("Content-Type", "application/json");
post.addHeader("Authorization",
"Bearer XXXXXXXXXXXXXXXXXXXXXXXXX");
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("title", "Test folder");
jsonObject
.addProperty("mimeType", "application/vnd.google-apps.folder");
post.setEntity(new StringEntity(jsonObject.toString()));
httpClient.execute(post);
}
Related
I'm trying to generate a token from an external website, the post request must be application/x-www-form-urlencoded but I'm getting errors. I assume that I'm not making the right call for the content type application/x-www-form-urlencoded but I can't figuer out how!!
PS: I'm using springboot with java 8
here is the code:
public String getNewAccesToken() {
//Initilazing variabels
try {
JsonObject properties = new JsonObject();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set("Cookie", cookie);
properties.addProperty("client_id", clientId);
properties.addProperty("client_secret", clientSecret);
properties.addProperty("grant_type", grantType);
HttpEntity<String> requestEntity = new HttpEntity<>(properties.toString(), headers);
log.debug(requestEntity);
ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.POST, requestEntity,
String.class);
log.debug("---------------------------------");
log.debug(response);
if (Util.isJSONValid(response.getBody())) {
JsonParser parser = new JsonParser();
JsonObject jsonString = (JsonObject) parser.parse(response.getBody());
return jsonString.get("accessToken").getAsString();
} else {
error.setCode(ConstantGateway.JSON_ERROR_CODE);
error.setMessage(ConstantGateway.JSON_ERROR_STATUS);
return error.toString();
}
} catch (HttpStatusCodeException e) {
error.setCode(String.valueOf(e.getStatusCode().value()));
error.setMessage(e.getResponseBodyAsString());
return error.toString();
} catch (Exception e) {
// TODO: handle exception
error.setCode(ConstantGateway.IL_INTERNAL_ERROR_CODE);
error.setMessage(e.getMessage());
return error.toString();
}
}
and here is what I get when calling this function:
org.zwz.vas.internal.api.model.ErrorModel#16dd359c
However when I make the call from Postman I get the right response which is :
{
"access_token": "RdWt3DNIfxmihnubGX0Fgfcb0KNHLZV79OfN9Y6Ky6Z3fxAfF_Pm7uP0jnFrG1fHplyBMZ74BIKleQ8jmswdGy4e87NV-uZsMzgS1nQAONc2nBxgU1_jkMBhL4vvIniJNd99oYNzGeanCYYki0yorrrlLrOGTncusv1BgFFHU_CBGuUtGmZYLfJAJW4XcZLhXMC9xpT2aWAvgRXZW69pOhfU1Fgs7aVwou85UVI2b4j1GfX0pCtJtluiTgXsuWqdck7_at1dqfopHpjWAywYrweStMXGm8T59nyQi_oXWmo",
"token_type": "bearer",
"expires_in": 1199
}
THANK YOU IN ADVANCE
I find the mistake I did, I was sending a wrong request format which is not compatibale with application/x-www-form-urlencoded type. The right code is below:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set("Cookie", cookie);
MultiValueMap<String, String> properties= new LinkedMultiValueMap<String, String>();
properties.add("client_id", clientId);
properties.add("client_secret", clientSecret);
properties.add("grant_type", grantType);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(properties, headers);
ResponseEntity<String> response = restTemplate.postForEntity( requestUrl, request , String.class );
Here's the error message:
400 Bad Request: [Unrecognized field "rep" (class org.keycloak.representations.idm.UserRepresentation), not marked as ignorable]
Here's some code:
public String createUser2() throws UnsupportedEncodingException, JSONException {
String adminToken = getAccessToken();
System.out.println("token: " + adminToken);
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
headers.set("Authorization", "Bearer " + adminToken);
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setFirstName("some");
userRepresentation.setLastName("user");
userRepresentation.setUsername("Some.User#somewhere.com");
userRepresentation.setEmail("Some.User#somewhere.com");
// Gson gson = new Gson();
// String jsonString = gson.toJson(userRepresentation);
MultiValueMap<String, UserRepresentation> map = new LinkedMultiValueMap<String, UserRepresentation>();
map.add("rep", userRepresentation);
HttpEntity<MultiValueMap<String, UserRepresentation>> request = new HttpEntity<>(map, headers);
String uri = "http://10.127.2.46:31680/auth/admin/realms/12xDemo/users";
System.out.println("URI: " + uri);
UserRepresentation response = (new RestTemplate()).postForObject(uri, request, UserRepresentation.class);
//JSONObject obj = new JSONObject(response);
return (new Gson()).toJson(response);
}
Looks like you should send HttpEntity<UserRepresentation>as a request. Cause your server code waits for this type and receives "rep": "UserRepresentation", tries to map it on actual UserRepresentation.class
File fileJson = new File("answer.json");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
**body.add("answer", fileJson);**
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String urlFinal = "url";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(urlFinal, requestEntity, String.class);
System.out.println(response);
The server returns me a 400 error saying that the file was not sent in the body. I was wondering if the problem is with my code or with the server.
The file is a JSON, which must be sent in Multipart-Form-data.
I left the urlFinal string with just "url" to put as an example, but there is a valid url, as I've already done tests.
You need add filename and BAOS to MultiValueMap body, add this:
File fileJson = new File("answer.json");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("filename", fileJson.getName());
body.add("file", new ByteArrayResource(Files.readAllBytes(fileJson.toPath()));
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String urlFinal = "url";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(urlFinal, requestEntity, String.class);
System.out.println(response);
But is not the best mode, because you can change your code for use this method:
#Service
public class FileUploadService {
private RestTemplate restTemplate;
#Autowired
public FileUploadService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
public void postFile(String filename, byte[] someByteArray) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// This nested HttpEntiy is important to create the correct
// Content-Disposition entry with metadata "name" and "filename"
MultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>();
ContentDisposition contentDisposition = ContentDisposition
.builder("form-data")
.name("file")
.filename(filename)
.build();
fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
HttpEntity<byte[]> fileEntity = new HttpEntity<>(someByteArray, fileMap);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", fileEntity);
HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(body, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(
"/urlToPostTo",
HttpMethod.POST,
requestEntity,
String.class);
} catch (HttpClientErrorException e) {
e.printStackTrace();
}
}
}
I've been reading the following and trying to send a POST request with Spring Boot, using RestTemplate.
HttpHeaders headers = new HttpHeaders();
headers.add("content-type", "application/x-www-form-urlencoded");
final String body = "clipmessage={ bridgeId: \"" + user.getBridgeId() + "\", clipCommand: { url: \"" + setLightState("3") + "\", method: \"PUT\", body: { \"on\": " + state.isOn() + " } } }";
final String url = API_ADDRESS + user.getAccessToken();
HttpEntity<String> entity = new HttpEntity<>(body, headers);
restTemplate.postForEntity(url, entity, String.class);
If I log the URL and the body and send the exact same in Postman, it succeeds. However, not when I send it from my Spring Boot application.
I'm guessing that special body has to be sent in some special way which that I am not aware off?
Anyone has any tips on what to try here next?
UPDATE 1:
I tried the MultiValueMap as suggested, but did not get that to work either.
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
final String body = "{ bridgeId: \"" + user.getBridgeId() + "\", clipCommand: { url: \"" + setLightState("3") + "\", method: \"PUT\", body: { \"on\": " + state.isOn() + " } } }";
map.add("clipmessage", body);
HttpEntity<String> entity = new HttpEntity<>(body, headers);
I have the same scenario. solved after using the following code :
restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
FormHttpMessageConverter formMessageConverter = new FormHttpMessageConverter();
messageConverters.add(formMessageConverter);
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
Parameters and headerpart
...
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("signature", "signature");
//other parameters
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(parameters, headers);
ResponseEntity<ResponseMessage> responseEntity = restTemplate.postForEntity(url, requestEntity, ResponseMessage.class);
ResponseMessage respMsg =responseEntity.getBody();
logMsg.append(",HTTP STATUS=").append(responseEntity.getStatusCode()).append(", RES:").append(marshal(respMsg));
I want to use Spring RestTemplate instead of Apache HttpClient for working with a remote API
With HttpClient
// build request JSON
JSONObject json = new JSONObject();
json.put("username", username);
json.put("serial", serial);
json.put("keyId", keyId);
json.put("otp", otp);
String json_req = json.toString();
// make HTTP request and get response
HttpPost request = new HttpPost(AuthServer);
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(json_req));
response = client.execute(request);
With RestTemplate
Map<String, String> paramMap = new HashMap<String,String>();
paramMap.put("username", userName);
paramMap.put("serial", serial);
paramMap.put("keyId", keyId);
paramMap.put("otp", otp);
String mapAsJson = new ObjectMapper().writeValueAsString(paramMap);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<String>(mapAsJson,requestHeaders);
try {
ResponseEntity<String> response = restTemplate.exchange(AuthServer, HttpMethod.POST, request, String.class);
return response.getHeaders();
} catch (HttpClientErrorException e) {
return null;
}
}
The code with HttpClient works but that with RestTemplate does not. I don't know how to use StringEntity in RestTemplate.
Spring version is 3.0.0, and JVM is 1.6.
RestTemplate is better suited to working with objects. As an example:
AuthenticationRequest.java
class AuthenticationRequest {
private String username;
private String serial;
private String key;
private String otp;
}
AuthenticationResponse.java
class AuthenticationResponse {
private boolean success;
}
AuthenticationCall.java
class AuthenticationCall {
public AuthenticationResponse execute(AuthenticationRequest payload) {
HttpEntity<AuthenticationRequest> request = new HttpEntity<AuthenticationRequest>(payload, new HttpHeaders());
return restTemplate.exchange("http://www.domain.com/api/endpoint"
, HttpMethod.POST
, request
, AuthenticationResponse.class).getBody();
}
}
These classes can be used as follows:
if(new AuthenticationCall().execute(authenticationRequest).isSuccess()) {
// Authentication succeeded.
}
else {
// Authentication failed.
}
All of this requires there to be a JSON library such as Jackson or GSON on the classpath.