Image share with linkedin v2 api not posting on page feed - java

Ok so this is my problem.. To share an image post via linkedin api, you first have to register your image file, you do that via a post request in which you send your binary file. Then you use the the image URN in the original request to submit your post. My request goes through, returns 201 code (which should be a successful request) but ends up not posting the image or the text. If i try to post only text, it works. I've tried registering my image using curl, and it posted on linkedin, so i think i'm not sending the binary file in a request properly, this is my request:
HttpClient client = HttpClientBuilder.create().build();
HttpPut request = new HttpPut(uploadUrl);
request.addHeader("Content-Type", "data/binary");
request.setHeader("X-Restli-Protocol-Version", "2.0.0");
request.setHeader("Authorization", "Bearer " + myToken);
File file = new File(pictureUrl);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addBinaryBody("upload-file", file);
request.setEntity(builder.build());
HttpResponse response = client.execute(request);
I get code 201 with this code, but it still doesn't post.
This is the curl example of the request that they give on Share API doc.
curl -i --upload-file /Users/peter/Desktop/superneatimage.png --header "Authorization: Bearer redacted" 'https://api.linkedin.com/mediaUpload/C5522AQGTYER3k3ByHQ/feedshare-uploadedImage/0?ca=vector_feedshare&cn=uploads&m=AQJbrN86Zm265gAAAWemyz2pxPSgONtBiZdchrgG872QltnfYjnMdb2j3A&app=1953784&sync=0&v=beta&ut=2H-IhpbfXrRow1'
Can you tell me what is wrong with my java equivalent?
Edit: Forgot to say i even tried calling curl from java, the same code i used in the terminal, and it still didn't work..
Process p;
try {
p = Runtime.getRuntime().exec("curl -i --upload-file" + " " + pictureUrl + " " + "--header \"Authorization: Bearer " + myToken + "\" '" + uploadUrl + "'");
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
String outputController = "";
while ((line = reader.readLine()) != null) {
outputController = outputController + '\n' + line;
}
System.out.println("out: ");
System.out.println(outputController);
return true;
} catch (IOException | InterruptedException ex) {
return false;
}
Output returned an empty String.
Edit2: Another funny thing, when i execute the main request, in which i send the text, and media urns that i get after submitting images, i get 201 again, like it's successful, and in the response i even get the post id. Then i try to use the another api endpoint and pull that post using the id i got from the response, and i get all the data, like the post is posted. It even says in the json that i get that lifecycle is PUBLISHED and the status of the medias is READY. Only thing that's different from the json an image post that is on linkedin is that the media object have thumbnails, and in this case they don't, it's just an empty json array.

I found curl to C# converter https://curl.olsh.me/
below is code snippet is generated:
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("PUT"), "https://api.linkedin.com/mediaUpload/C5522AQGTYER3k3ByHQ/feedshare-uploadedImage/0?ca=vector_feedshare&cn=uploads&m=AQJbrN86Zm265gAAAWemyz2pxPSgONtBiZdchrgG872QltnfYjnMdb2j3A&app=1953784&sync=0&v=beta&ut=2H-IhpbfXrRow1"))
{
request.Headers.TryAddWithoutValidation("Authorization", "Bearer redacted");
request.Content = new ByteArrayContent(File.ReadAllBytes("/test.png"));
var response = await httpClient.SendAsync(request);
}
}

Ok I've solved it, if anyone encounters the same problem, this is what i did wrong. In the request i added a multipart to request body, this is wrong, you just go RAW. So instead of
File file = new File(pictureUrl);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addBinaryBody("upload-file", file);
request.setEntity(builder.build());
you just put
request.setEntity(new FileEntity(new File(pictureUrl), ContentType.create(picture.getContentType())));
and then everything goes on ok.

Not familiar with Java, but I had the same problem using Ruby and I fixed it by adding the MIME type of the image I was uploading as the Content-Type in the request headers. So in your specific case it would be:
request.addHeader("Content-Type", "image/png");
Also take a look at my solution using Ruby's RestClient and Minimagick: https://stackoverflow.com/a/54902863/7844946

I was writing with kotlin and after struggling with the same problem for a long time I managed to fix it, for those struggling with the similar problem, I'll leave my sample codes below. For the visual you need to provide a direct URI path, also pay attention to the header structure.
Here are the codes.
var file = File(ImageURI)
val urls = URL(url)
var connection: HttpsURLConnection = urls.openConnection() as HttpsURLConnection
connection.setRequestProperty("Authorization","Bearer " + accesToken)
connection.setRequestProperty("Content-Type", "image/png")
connection.setRequestProperty("cache-control", "no-cache")
connection.setRequestProperty("X-Restli-Protocol-Version", "2.0.0")
connection.requestMethod = "POST"
connection.doOutput = true
connection.doInput = true
var request = DataOutputStream(connection.outputStream)
request.write(file.readBytes())
request.flush()
println("Response: "+connection.responseCode)

Related

401 for PostRequest with HttpURLConnection

UPDATE
My problem turned out to be due to some super-class weirdness. However, the urlEncode in this example is wrong. In this case it was also unnecessary. If needed it should be done by each key and value without encoding & or =.
Other than that, this code is correct.
The (scala) code below fails because the server does not seem to receive the body.
The warning on the server log is:
WARNING *** OAuthTokenProcessor caught a OAuthRequestProblemException with message OAuthProblemException{error='invalid_request', description='Missing grant_type parameter value', uri='null', state='null', scope='null', redirectUri='null', responseStatus=0, parameters={}}
I am certain that the params are correct. The url is correct, The headers are correct
(This call already works in Postman and Python)
Any hints deeply appreciated!
private def doPostRequest(): Unit ={
try {
val connectionforPost = (new URL(url)).openConnection.asInstanceOf[HttpURLConnection]
val params = "grant_type=password&client_id=xxxx&client_secret=seccret&username=admin&password=xxxx"
val encodedString = URLEncoder.encode(params,"UTF-8")
import java.nio.charset.StandardCharsets
val postData = encodedString.getBytes(StandardCharsets.UTF_8)
val contentlength = Integer.toString(encodedString.length)
connectionforPost.setConnectTimeout(50000)
connectionforPost.setReadTimeout(50000)
connectionforPost.setRequestMethod("POST")
connectionforPost.setDoOutput(true)
connectionforPost.setDoInput(true)
connectionforPost.setInstanceFollowRedirects(false)
connectionforPost.setRequestProperty("User-agent", "test bot")
connectionforPost.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
connectionforPost.setRequestProperty("Accept", "*/*")
connectionforPost.setRequestProperty("Connection", "keep-alive")
connectionforPost.setRequestProperty("Content-Length", contentlength)
connectionforPost.setRequestProperty("Accept-Encoding", "gzip, deflate")
connectionforPost.setUseCaches(false)
import java.io.DataOutputStream
val wr = new DataOutputStream(connectionforPost.getOutputStream)
wr.write(postData)
if (wr != null) wr.close()
} catch{
case e:Exception => logger.error(e.getMessage,e)
throw e
}
}
It appears that the server complains that you are not logged in. You get an authentication problem exception. How it usually works is that you send a request to the server to login with the user name and password. If the server authenticates you (recognizes your user name and password as valid ones) it sends you a response with an authentication token in the headers. You may see those 2 headers in your response to successful login:
access-control-expose-headers=[Authorization], Authorization=[Bearer ....]
This means that in all your subsequent requests you will need to add a header "Authorization" that will hold the value "Bearer ....". Then the server will recognize this request as coming from an authenticated user.
Also, I suggest that you may use a 3d party HTTP client. Some well-known clients are Appache HTTP client and OK HTTP client. I use my own Http client that I wrote. It is also available as part of a MgntUtils Open Source Library. Here is the link to Maven artifacts and it is also available on Github with source code and JavaDoc. And here is a javadoc page for HttpClient. Here is the code sample on how you may obtain the token and use it:
try {
HttpClient loginClient = new HttpClient();
loginClient.setConnectionUrl("http://your_url/login");
loginClient.setRequestProperty("accept", "application/json;charset=UTF-8");
loginClient.setContentType("application/json");
String result = loginClient.sendHttpRequest(HttpMethod.POST, "{ \"username\": \"your_user_name\", \"password\": \"Your_password\"}");
System.out.println(result);
System.out.println("HTTP " + loginClient.getLastResponseCode() + " " + loginClient.getLastResponseMessage());
System.out.println("Response headers: " + loginClient.getLastResponseHeaders());
String accessControlExposeHeader = loginClient.getLastResponseHeader("access-control-expose-headers").get(0);
String accessKey = loginClient.getLastResponseHeader(accessControlExposeHeader).get(0);
HttpClient workingClient = new HttpClient();
workingClient.setRequestProperty("accept", "application/json;charset=UTF-8");
workingClient.setContentType("application/json");
workingClient.setRequestProperty(accessControlExposeHeader, accessKey);
workingClient.setConnectionUrl("http://yourUrl/yourPath");
System.out.println(workingClient.sendHttpRequest(HttpMethod.GET));
} catch (IOException e) {
System.out.println(TextUtils.getStacktrace(e));
}

Linked in unable to to retrieve access token missing required parameters error

I have been working on integrating my application with LinkedIn by following the documentation located here. I have created my application in LinkedIn and am able to successfully retrieve the authorization code but I am getting the following error when trying to get the access token :
{"error_description":"missing required parameters, includes an invalid
parameter value, parameter more than once. : Unable to retrieve access token : appId or redirect uri does not match authorization code or authorization code expired","error":"invalid_request"}
I have verified the following to be true:
The redirect uri is the same for the authorization request and access token request
I am using the authorization code within 20 seconds of it being issued.
I am including "Content-Type:application/x-www-form-urlencoded" in the request header
Some things I have tried with no success:
Posting the request with the parameters as part of the url
Posting the request with parameters as part of the body
sending the redirect uri as both encoded and plain text
using a get request instead of a post.
Here is my current code:
linkedin_access_token_url = "https://www.linkedin.com/uas/oauth2/accessToken?"+
"grant_type=authorization_code"+
"&code="+ authCode
+ "&redirect_uri=https://localhost:8090/ProfileSetup/linkedInAuth.jsp
+ "&client_id=" + linkedin_client_id
+ "&client_secret=" + linkedin_client_secret;
HttpClient http = new DefaultHttpClient();
HttpPost httppost = new HttpPost(linkedin_access_token_url);
try {
httppost.setHeader("Content-Type", "application/x-www-form-urlencoded");
HttpResponse response = http.execute(httppost);
HttpEntity entity = response.getEntity();
System.out.println("status code " +
response.getStatusLine().getStatusCode());
System.out.println("statusreason"+
response.getStatusLine().getReasonPhrase());
InputStream stream = entity.getContent();
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(stream));
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {
}
String resp = sb.toString();
System.out.println("response " + resp);
} catch (Exception ex) {
System.out.println("linked in HttpResponse Error: " + ex);
} finally {
httppost.releaseConnection();
}
And the authorization url (actual client id is sent in place of linkedin_client_id):
https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=linkedin_client_id&redirect_uri=https://localhost:8090/ProfileSetup/linkedInAuth.jsp&state=0kcmjj5504tpgb9&scope=r_basicprofile
Does anyone see what I am doing wrong? If I take this compiled url and paste it in the browser, I am able to retrieve an access token without any issue. Is there a problem with my request?
Looks like you are including all of the parameters for the request as attributes in the URL, rather than in the POST body as x-www-form-urlencoded elements.
Check out this other Stack thread for details on how to send the values in the request body rather than as URL attributes:
Sending HTTP POST Request In Java
I was finally able to figure out what was wrong with my app. My local environment was not configured correctly for https. Moving the code onto our dev box set up with https fixed the issue.

uploading files to a dataset in CKAN / datahub.io through a Java client

I am testing the uploading of files to a dataset on CKAN / datahub.io through a Java client of the API.
public String uploadFile()
throws CKANException {
String returned_json = this._connection.MultiPartPost("", "");
System.out.println("r: " + returned_json);
return returned_json;
}
and
protected String MultiPartPost(String path, String data)
throws CKANException {
URL url = null;
try {
url = new URL(this.m_host + ":" + this.m_port + path);
} catch (MalformedURLException mue) {
System.err.println(mue);
return null;
}
String body = "";
HttpClient httpclient = new DefaultHttpClient();
try {
String fileName = "D:\\test.jpg";
FileBody bin = new FileBody(new File(fileName),"image/jpeg");
StringBody comment = new StringBody("Filename: " + fileName);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("bin", bin);
reqEntity.addPart("comment", comment);
HttpPost postRequest = new HttpPost("http://datahub.io/api/storage/auth/form/2013-01-24T130158/test.jpg");
postRequest.setEntity(reqEntity);
postRequest.setHeader("X-CKAN-API-Key", this._apikey);
HttpResponse response = httpclient.execute(postRequest);
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("status code: " + statusCode);
BufferedReader br = new BufferedReader(
new InputStreamReader((response.getEntity().getContent())));
String line;
while ((line = br.readLine()) != null) {
body += line;
}
System.out.println("body: " + body);
} catch (IOException ioe) {
System.out.println(ioe);
} finally {
httpclient.getConnectionManager().shutdown();
}
return body;
}
2 responses I get to my POST request:
a 413 error ("request entity too large") when the jpeg I try to upload is 2.83 Mb. This disappears when I shrink the file to a smaller size. Is there a limit to file size uploads?
a 500 error ("internal server error"). This is where I am stuck. It might have to do with the fact that my dataset on datahub.io is not "datastore enabled"? (I see a disabled "Data API" button next to my resource files in the dataset, with a tooltip saying:
"Data API is unavailable for this resource as DataStore is disabled"
=> is it a possible reason for this 500 error? If so, how could I enable it from the client side? (pointers to Python code would be useful!)
Thx!
PS: the dataset I am using for testing purposes: http://datahub.io/dataset/testapi
Only someone with access to the exception log could tell you why the 500 is occurring.
However, I'd check your request is the same as what you'd get from the python client that was written alongside the datastore: https://github.com/okfn/ckanclient/blob/master/ckanclient/init.py#L546
You're sending the "bin" image buffer and "comment" file_key in your multipart request. Note the file_key must be changed for every upload, so add in a timestamp or something. And maybe you need to add in a Content-Type: for the binary.
I have been going through the same kind of troubles as the poster of this question. After quite a bit of trial and error, I came up with a solution to the problem. In my case, I had some control over the CKAN repository that I wanted to upload to. If you don't, your problem might be impossible to solve...
I assume you are using the 1.8 version of CKAN?
First of all, check whether the CKAN repository has been set up to allow file upload and if not, configure it to allow that. This can be done on the server using the steps posted here: http://docs.ckan.org/en/ckan-1.8/filestore.html#local-file-storage
The 413 error that you mentioned should be adressed next. This has to do with the general configuration of the server. In my case, the CKAN was hosted through nginx. I added a "client_max_body_size 100M" line to the nginx.conf file. See this post for instance: http://recursive-design.com/blog/2009/11/18/nginx-error-413-request-entity-too-large/
Then there is only the 500 error left. At the time of this writing, the api documentation of CKAN is still a little immature... It does indeed say that you have to build a request like you have made for file upload. However, this request is just to ask for permission for the file upload. If your credentials check out for file upload (not every user may be allowed to upload files), the response holds an object telling you where to send your file to... Because of the unclear api, you ended up merging these two requests.
The following scenario shows a follow up of two requests to handle the file upload. It might be that some steps in the scenario work out differently in your case, because of a repository that has been set up a little differently. If you get error messages, please be sure to check the response's body for clues!
Here is the authentication request that I used:
String body = "";
String generatedFilename=null;
HttpClient httpclient = new DefaultHttpClient();
try {
// create new identifier for every file, use time
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyyMMMddHHmmss");
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String date=dateFormatGmt.format(new Date());
generatedFilename=date +"/"+filename;
HttpGet getRequest = new HttpGet(this.CKANrepos+ "/api/storage/auth/form/"+generatedFilename);
getRequest.setHeader(CKANapiHeader, this.CKANapi);
HttpResponse response = httpclient.execute(getRequest);
int statusCode = response.getStatusLine().getStatusCode();
BufferedReader br = new BufferedReader(
new InputStreamReader((response.getEntity().getContent())));
String line;
while ((line = br.readLine()) != null) {
body += line;
}
if(statusCode!=200){
throw new IllegalStateException("File reservation failed, server responded with code: "+statusCode+
"\n\nThe message was: "+body);
}
}finally {
httpclient.getConnectionManager().shutdown();
}
Now, if all goes well, the server responds with a json object holding the parameters to use when doing the actual file upload. In my case, the object looked like:
{file_key:"some-filename-to-use-when-uploading"}
Be sure to check the json object though, as I'm given to understand that there may be custom ckan repositories that require more or different parameters.
These responses can then be used in the actual file upload:
File file = new File("/tmp/file.rdf");
String body = "";
HttpClient httpclient = new DefaultHttpClient();
try {
FileBody bin = new FileBody(file,"application/rdf+xml");
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("file", bin);
reqEntity.addPart("key", new StringBody(filename));
HttpPost postRequest = new HttpPost(this.CKANrepos+"/storage/upload_handle");
postRequest.setEntity(reqEntity);
postRequest.setHeader(CKANapiHeader, this.CKANapi);
HttpResponse response = httpclient.execute(postRequest);
int statusCode = response.getStatusLine().getStatusCode();
BufferedReader br = new BufferedReader(
new InputStreamReader((response.getEntity().getContent())));
String line;
while ((line = br.readLine()) != null) {
body += line;
}
if(statusCode!=200){
getWindow().showNotification("Upload Statuscode: "+statusCode,
body,
Window.Notification.TYPE_ERROR_MESSAGE);
}
}finally {
httpclient.getConnectionManager().shutdown();
}
as you can see, the file_key property has now been transformed into the simple 'key' property. I don't know why.
This will get your file uploaded. The response to this upload request will hold a json object telling you where the file got uploaded to. edit: actually it seems that my ckan responded with a simple html page to tell me that the file got uploaded... I had to parse the page to confirm that the file was uploaded correctly :(
In my case, the file was at
this.CKANrepos +"/storage/f/"+location
where location is the filename returned in the authentication phase.
In the previous code fragments:
//the location of your ckan repository, including /api and possibly version, e.g.
this.CKANrepos = "http://datahub.io/api/3/";
this.CKANapiHeader="X-CKAN-API-Key";
this.CKANapi = "your ckan api key here";

Getting URL after a redirect using HttpClient.Execute(HttpGet)

I have searched for a while and I am not finding a clear answer. I am trying to log into a webstie.
https://hrlink.healthnet.com/
This website redirects to a login page that is not consitent. I have to post my login credentials to the redirected URL.
Im am trying to code this in Java but I do not understand how to get the URL from the response. It may look a bit messy but I have it this way while I am testing.
HttpGet httpget = new HttpGet("https://hrlink.healthnet.com/");
HttpResponse response = httpclient.execute(httpget);HttpEntity entity = response.getEntity();
String redirectURL = "";
for(org.apache.http.Header header : response.getHeaders("Location")) {
redirectURL += "Location: " + header.getValue()) + "\r\n";
}
InputStream is;
is = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
String result = sb.toString();
I know i get redirected because my result string shows be the actual login page but I am not able to get the new URL.
In FireFox I am using TamperData. When I navigate to this website https://hrlink.healthnet.com/ I have a GET with a 302 - Found and the Location of the Login Page. Then another GET to the actual Login Page
Any help is greatly appreciated thank you.
Check out w3c documentation:
10.3.3 302 Found
The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).
If the 302 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.
One solution is to use POST method to break auto-redirecting at client side:
HttpPost request1 = new HttpPost("https://hrlink.healthnet.com/");
HttpResponse response1 = httpclient.execute(request1);
// expect a 302 response.
if (response1.getStatusLine().getStatusCode() == 302) {
String redirectURL = response1.getFirstHeader("Location").getValue();
// no auto-redirecting at client side, need manual send the request.
HttpGet request2 = new HttpGet(redirectURL);
HttpResponse response2 = httpclient.execute(request2);
... ...
}
Hope this helps.

How to send a JSONObject to a REST service?

Retrieving data from the REST Server works well, but if I want to post an object it doesn't work:
public static void postJSONObject(int store_type, FavoriteItem favorite, String token, String objectName) {
String url = "";
switch(store_type) {
case STORE_PROJECT:
url = URL_STORE_PROJECT_PART1 + token + URL_STORE_PROJECT_PART2;
//data = favorite.getAsJSONObject();
break;
}
HttpClient httpClient = new DefaultHttpClient();
HttpPost postMethod = new HttpPost(url);
try {
HttpEntity entity = new StringEntity("{\"ID\":0,\"Name\":\"Mein Projekt10\"}");
postMethod.setEntity(entity);
HttpResponse response = httpClient.execute(postMethod);
Log.i("JSONStore", "Post request, to URL: " + url);
System.out.println("Status code: " + response.getStatusLine().getStatusCode());
} catch (ClientProtocolException e) {
I always get a 400 Error Code. Does anybody know whats wrong?
I have working C# code, but I can't convert:
System.Net.WebRequest wr = System.Net.HttpWebRequest.Create("http://localhost:51273/WSUser.svc/pak3omxtEuLrzHSUSbQP/project");
wr.Method = "POST";
string data = "{\"ID\":1,\"Name\":\"Mein Projekt\"}";
byte [] d = UTF8Encoding.UTF8.GetBytes(data);
wr.ContentLength = d.Length;
wr.ContentType = "application/json";
wr.GetRequestStream().Write(d, 0, d.Length);
System.Net.WebResponse wresp = wr.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(wresp.GetResponseStream());
string line = sr.ReadToEnd();
Try setting the content type header:
postMethod.addRequestHeader("Content-Type", "application/json");
Btw, I strongly recommend Jersey. It has a REST client library which makes these kind of things much easier and more readable
Your C# is different than your Java, and not just in syntax.
Your C# sends an application/json entity to the server via HTTP POST. I'll leave it up to HTTP purists as to whether that's appropriate use of POST (vs. PUT).
Your Java creates a form, with a field of jsonString (whose value is the JSON), and sends an application/x-www-form-urlencoded entity to the server containing that form.
I would go right to the server err_log or equivelant error log. The server knows why it rejected your request. If you don't have access, set up your own test server and duplicate the issue there so you can review the logs =)

Categories