I am fairly new to programming with Java but am interested in creating a program that allows for connection to the Spotify API. I am using the Client Credential Flow authorization process but keep getting java.io.IOException: insufficient data written exception when trying to reach the access token. I cannot figure out what information I am missing to complete the request.
I found a YouTube video of the same process being completed in Python and they utilized the requests feature and .json() to receive the access token. Is there a similar way to complete this in Java?
try {
String str = "application/x-www-form-urlencoded";
byte[] hold = str.getBytes();
//create url
URL url = new URL(tokenURL);
//open connection to url
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
//setup post headers and body
conn.setRequestMethod("POST");
conn.setFixedLengthStreamingMode(32);
conn.setRequestProperty("Authorization",String.format("Basic %s", clientCredEncode));
conn.setRequestProperty("grant_type", "client_credentials");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36");
//validate connection
int val = conn.getResponseCode();
String response = conn.getResponseMessage();
System.out.println("response code: " + val);
System.out.println("response: " + response);
}catch(Exception e){
System.out.println("error: " + e);
conn.disconnect();
}
PYTHON CODE
This code performs the action in python.
def spotifyAuth(clientID, clientSecret):
clientCred = f"{clientID}:{clientSecret}"
encodedClient = base64.b64encode(clientCred.encode())
tokenURL = "https://accounts.spotify.com/api/token"
method = "POST"
tokenData = {"grant_type" : "client_credentials"}
tokenHeader = {"Authorization" : f"Basic {encodedClient.decode()}"}
r = requests.post(tokenURL, data=tokenData, headers=tokenHeader)
tokenResponse = r.json()
accessToken = tokenResponse['access_token']
expires = tokenResponse['expires_in']
return accessToken, expires
Thanks to Rup I was able to identify the issue. I was not properly sending anything with the POST. I added .getOutputStream() so send the request and .getInputStream() to receive the response.
//create url access point
URL url = new URL(tokenURL);
//open http connection to url
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
//setup post function and request headers
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization",String.format("Basic %s", clientCredEncode));
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//set body for posting
String body = "grant_type=client_credentials";
//calculate and set content length
byte[] out = body.getBytes(StandardCharsets.UTF_8);
int length = out.length;
conn.setFixedLengthStreamingMode(length);
//connect to http
conn.connect();
//}
//send bytes to spotify
try(OutputStream os = conn.getOutputStream()) {
os.write(out);
}
//receive access token
InputStream result = conn.getInputStream();
s = new String(result.readAllBytes());
//System.out.println(s);
For anyone whos looking for the same, Here's a better one
import com.google.gson.JsonParser;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class SpotifyToken {
public String accessToken = "";
public String expiresIn = "";
public void get() throws IOException {
URL url = new URL(Endpoints.TOKEN);
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setRequestMethod("POST");
http.setDoOutput(true);
http.setRequestProperty("content-type", "application/x-www-form-urlencoded");
String data = "grant_type=client_credentials&client_id=" + Endpoints.CLIENT_ID + "&client_secret=" + Endpoints.CLIENT_SECRET + "";
byte[] out = data.getBytes(StandardCharsets.UTF_8);
OutputStream stream = http.getOutputStream();
stream.write(out);
BufferedReader Lines = new BufferedReader(new InputStreamReader(http.getInputStream()));
String currentLine = Lines.readLine();
StringBuilder response = new StringBuilder();
while (currentLine != null) {
response.append(currentLine).append("\n");
currentLine = Lines.readLine();
}
this.accessToken = String.valueOf(JsonParser.parseString(String.valueOf(response)).getAsJsonObject().getAsJsonObject("access_token"));
this.expiresIn = String.valueOf(JsonParser.parseString(String.valueOf(response)).getAsJsonObject().getAsJsonObject("expires_in"));
http.disconnect();
}
}
The class Endpoints.java will be
public class Endpoints {
public static final String CLIENT_ID = "YOUR_CLIENT_ID";
public static final String CLIENT_SECRET = "YOUR_CLIENT_SECRET";
public static final String TOKEN = "https://accounts.spotify.com/api/token";
}
If it interests you, here is the cURL command for the same:
curl --request POST \
--url 'https://accounts.spotify.com/api/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data client_id=YOUR_CLIENT_ID \
--data client_secret=YOUR_CLIENT_SECRET \
I used this tool to convert cURL to java code.
Related
I'm using client credential secret to run API on Microsoft Endpoint (Intune).
Example used from link.
Getting access token. (Working)
Get android Managed App Protections. (Working using GET HTTP Method)
Patch Request. (Not Working)
The examples do not mention any PATCH or POST request, hence need some help for it.
I tried the below code snippet but it fails.
private void setAndroidModels(final String accessToken, final String policyId, final String modelList)
throws IOException {
URL url = new URL(
"https://graph.microsoft.com/beta/deviceAppManagement/androidManagedAppProtections/" + policyId);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("PATCH");
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = modelList.getBytes();
os.write(input, 0, input.length);
}
int httpResponseCode = conn.getResponseCode();
System.out.println("POST Response Code : " + httpResponseCode);
System.out.println("POST Response Message : " + conn.getResponseMessage());
}
Result : Exception in thread "main" java.net.ProtocolException: Invalid HTTP method: PATCH
Also tried
private void setAndroidModels(final String accessToken, final String policyId, final String modelList)
throws IOException {
URL url = new URL(
"https://graph.microsoft.com/beta/deviceAppManagement/androidManagedAppProtections/" + policyId);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("X-HTTP-Method-Override", "PATCH");
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = modelList.getBytes();
os.write(input, 0, input.length);
}
int httpResponseCode = conn.getResponseCode();
System.out.println("POST Response Code : " + httpResponseCode);
System.out.println("POST Response Message : " + conn.getResponseMessage());
}
Result :
POST Response Code : 400
POST Response Message: Bad Request
How can I get the client credential secret logic working for POST and PATCH HTTP Methods?
We can directly call patch request in MS Graph.
When creating PATCH requests to the API, you need to create a new
PATCH object that contains only the information you want to update.
This should be distinct from any objects you receive from the service
from a GET or a POST.
Please refer Document for more details.
For example, Patch rquest to user
User realMe = graphClient.me().buildRequest().get();
User patchMe = new User();
patchMe.givenName = "Beth";
realMe = graphClient
.users(realMe.userPrincipalName)
.buildRequest()
.patch(patchMe);
how to parse the JSON response from an api url using java? When I run the below code
I am getting SocketTimeoutException. but when browse the URL in the Chrome and Microsoft Edge i am getting the JSON data.
url:"https://www.nseindia.com/api/quote-derivative?symbol=RELIANCE"
(use only for testing purpose. not for commercial use)
Java Code:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.JSONObject;
public class HTTP_Request {
public static void main(String[] args) {
try {
Send_HTTP_Request.call_me();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void call_me() throws Exception {
String url ="https://www.nseindia.com/api/quote-derivative?symbol=RELIANCE";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// optional default is GET
con.setRequestMethod("GET");
//add request header
con.setRequestProperty("User-Agent", "Mozilla/5.0");
int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + url);
System.out.println("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
//print in String
System.out.println(response.toString());
//Read JSON response and print
// JSONObject myResponse = new JSONObject(response.toString());
// System.out.println("result after Reading JSON Response");
// System.out.println("origin- "+myResponse.getString("origin"));
}
}
look what happens when you call the provided url from a shell curl:
> $ curl 'https://www.nseindia.com/api/quote-derivative?symbol=RELIANCE' [±master ●●▴]
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>
You don't have permission to access "http://www.nseindia.com/api/quote-derivative?" on this server.<P>
Reference #18.15b5655f.1595338785.264b3809
</BODY>
</HTML>
So, I guess nseindia is blocking the request because is not coming from a browser.
But, if you try this:
curl 'https://www.nseindia.com/api/quote-derivative?symbol=RELIANCE' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Connection: keep-alive' -H 'Cookie: ak_bmsc=D709927FAB6A44F1C243E20E9199ABA35F65B5158B040000E1EF165F44B95756~plIifDJa9J1EHznQiR/zQpjmGEFWOE88N83Yuqa0oOcgvvYh7eLlgD5MSFVhCZZOObVMZ7UDwEzmOQ2V2lJ6W9pPf6zEbQT1Je27i6h3hrAIyBYFMplV1EDo7rSLxXmCk+HGL3ynHmuPJsePDPD7WTljjTMnM1qpvixsCMEtM1BlmVmuXQijd8cKjWxLeLaf/cNAEJB6VC7SXOq+j6uj6oi9xF/Z2NYB905XnUg9YppXM=' -H 'Upgrade-Insecure-Requests: 1' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
you get a json in the response.
{"info":{"symbol":"RELIANCE","companyName":"Reliance Industries Limited",.......}
So you need to add a couple of HTTP Headers to your request, something like:
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
con.setRequestProperty("Accept", "text/html");
I am trying to implement a client that logins first and do some staff.
This is my curl request:
curl -v https://api.example.com/api-token-auth/ \
-H "Accept: application/json" \
-d "username=myusername&password=mypassword"
I want to convert it into java code. Here is what I have tried:
HttpURLConnection conn;
URL obj = new URL("https://api.example.com/api-token-auth/");
URL obj = new URL(quoteURL);
conn = (HttpURLConnection) obj.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
String userpass = "username=myusername" + "&" + "password=mypassword";
String basicAuth = new String(Base64.getEncoder().encode(userpass.getBytes()));
conn.setRequestProperty("Authorization", basicAuth);
conn.setRequestProperty( "Accept", "*/*" );
conn.setRequestProperty( "Accept-Encoding", "gzip, deflate" );
conn.setRequestProperty( "Accept-Language", "en;q=1, fr;q=0.9, de;q=0.8,ja;q=0.7, nl;q=0.6, it;q=0.5" );
conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded; charset=utf-8" );
conn.setRequestProperty( "API-Version", "1.3.0" );
conn.setRequestProperty("Connection", "keep-alive");
conn.setRequestProperty( "Accept", "*/*" );
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.connect();
InputStreamReader inputStreamReader = new InputStreamReader(conn.getInputStream());
BufferedReader in = new BufferedReader(inputStreamReader);
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
conn.disconnect();
return response;
Then I receive this error:
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 400 for URL: https://api.example.com/api-token-auth/
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1839)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
I have tried several possible solutions, but no luck. I could nit find what I am doing wrong.
your curl request isn't actually doing HTTP basicAuth (which is what your example code is trying to do) - it's just POSTing the -d argument to the server (as a url encoded body)
so
get rid of all the setRequestProperty() stuff (it's not needed)
use con.setContentType("application/x-www-form-urlencoded") [which is arguably a bit cleaner]
write the userpass string to con.getOutputStream() [no need to base64 encode...again, this has nothing to do w/ http basicAuth]
for example, your curl command issues the following HTTP request
POST /api-token-auth/ HTTP/1.1
Host: api.example.com
User-Agent: curl/7.49.1
Accept: application/json
Content-Length: 39
Content-Type: application/x-www-form-urlencoded
username=myusername&password=mypassword
the following Java program would execute pretty much the EXACT same request
public class SO {
public static void main(String[] args) throws Exception {
String rsp = curl("http://axrsgpar0019:13080/api-token-auth/", "application/json", "username=myusername&password=mypassword");
}
public static String curl(String url, String accepts, String minusD) throws Exception {
HttpURLConnection con = (HttpURLConnection)new URL(url).openConnection();
con.setDoOutput(true);
con.setRequestProperty("Accept", accepts);
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
con.getOutputStream().write(minusD.getBytes());
con.getOutputStream().close();
ByteArrayOutputStream rspBuff = new ByteArrayOutputStream();
InputStream rspStream = con.getInputStream();
int c;
while ((c = rspStream.read()) > 0) {
rspBuff.write(c);
}
rspStream.close();
return new String(rspBuff.toByteArray());
}
}
generates the following HTTP request (only difference is User-Agent and keep-alive..which should be insignificant)
POST /api-token-auth/ HTTP/1.1
Accept: application/json
Content-Type: application/x-www-form-urlencoded
User-Agent: Java/1.8.0_91
Host: api.example.com
Connection: keep-alive
Content-Length: 39
username=myusername&password=mypassword
I am trying to connect to an api written in php from a java client.
For simplicity of the issue, I reduced the api to the following: (which simply returns the request given to the server)
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
define('DATA_PATH', realpath(dirname(__FILE__).'/data'));
$applications = array(
'APP001' => '28e336ac6c9423d946ba02d19c6a2632', //randomly generated app key for php client
'APP002' => '38e336ac6c9423d946ba02d19c6a2632' // for java app
);
require_once 'models/TodoItem.php';
echo"request";
foreach ($_REQUEST as $result) {
echo $result;
echo "<br>";
}
echo"end";
exit();
I am sending the request as follows: (string param is the string in the code snippet after this)
URL url;
HttpURLConnection connection = null;
try {
url = new URL(APP_URI);
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", "" +
Integer.toString(param.getBytes().length));
connection.setRequestProperty("Content-Language", "en-US");
connection.setUseCaches (false);
connection.setDoInput(true);
connection.setDoOutput(true);
//Send request
DataOutputStream wr = new DataOutputStream (
connection.getOutputStream ());
wr.writeBytes (param);
wr.flush ();
wr.close ();
//Get Response
InputStream is = connection.getInputStream();
// read from input stream
The request string being passed is as follows: (a json object with 2 params, one of which is another json object)
{"app_id":"APP002","enc_request":"{\"username\":\"nikko\",\"action\":\"checkUser\",\"userpass\":\"test1234\",\"controller\":\"todo\"}"}
The reply is as follows, which consist only of the start and end tags I'm manually echoing and no content:
requestend
Why am I not getting any content on the server side?
I ended up using apache's httpclient api. By combining the answers from the following questions: Sending HTTP POST Request In Java
and What's the recommended way to get the HTTP response as a String when using Apache's HTTP Client? I go the following solution.
Note: The app_id and enc_request which i was sending as part of json are now as part of a namedpair, which adheres to the array being expected on the server side. Hence, the param string is now only:
{"username":"nikko","action":"checkUser","userpass":"test1234","controller":"todo"}
The code is as follows:
public static String excutePost(String[][] urlParameters) {
try {
String param = encode(urlParameters);
HttpClient httpclient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(APP_URI);
List<NameValuePair> params = new ArrayList<NameValuePair>(2);
params.add(new BasicNameValuePair("app_id", APP_NAME));
params.add(new BasicNameValuePair("enc_request", param));
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) {
String res = EntityUtils.toString(entity);
return res;
}
} catch (IOException e) {
return null;
}
return null;
}
I'm trying to establish a Connection via HTTPS. I also set the "Authorization" property in the Request Header to Basic and provide an encoded auth string accordingly.
I checked with the Firefox Plugin HttpRequester and everythign works fine, which means I entered the url, choose "GET" as request method, add the Authorization to the header and after pressing submit I get back some xml which only a properly authorized user should get.
Unfortunately I can neither provide you with the actual auth info nor the real url in the SSCCE. However, I can tell you, that the Auth seems to work, since I get a 200 response. I also changed the Auth to a wrong value and get a "401 Authorization Required" response then.
It actually seems like the "?myparam=xyz" is somehow cut off, because when I remove this parameter from the url and test with Firefox HttpRequester again I get the same response as in Java.
Unfortunately I have no access to "theirdomain.com", so I don't know what's happending on the server side. But since it works with the Firefox HttpRequester, it should also work with Java.
What could be the reason? Thanks for your help!
EDIT:
I changed the url to "https://www.google.com/search?q=foo" and commented this line:
//con.setRequestProperty("Authorization", auth);
I can see from the returned string, that google received the "foo". So apparently the combination of Authorization and get parameter seems to be the problem, since both separately work fine.
SSCCE:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
public class HttpRequest
{
/**
* #param args
*/
public static void main(final String[] args)
{
System.out.println("start request");
final String urlString = "https://theirdomain.com/foo/bar/bob?myparam=xyz";
final String auth = "Basic XyzxYzxYZxYzxyzXYzxY==";
HttpsURLConnection con;
try
{
final URL url = new URL(urlString);
con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty("Authorization", auth);
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0");
con.setRequestMethod("GET");
// con.setDoOutput(true);
con.connect();
final int responseCode = con.getResponseCode();
if (responseCode != 200)
System.out.println("Server responded with code " + responseCode + " " + con.getResponseMessage());
else
{
System.out.println("Starting to read...");
final InputStream inStream = con.getInputStream();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
while (inStream != null && (c = inStream.read()) != -1)
{
baos.write(c);
}
System.out.println(new String(baos.toByteArray()));
}
}
catch (final IOException e)
{
System.out.println("could not open an HTTP connection to url: " + urlString);
e.printStackTrace();
}
finally
{
System.out.println("end request");
}
}
}
Have you tried adding
con.setRequestProperty("myparam", "xyz"); to your code?