I am creating an Android app that uploads basic text files to a user's onedrive by using the Microsoft Graph API.
I have discovered by using https://developer.microsoft.com/en-us/graph/graph-explorer to create files using a PUT request with the link "https://graph.microsoft.com/v1.0/me/drive/root:/Test.txt:/children", With the header "Content-Type" "text/plain", and filling the request body with the text to go into the text file. Using the graph explorer, this works without any issues, however when testing my code with all of these things, it always gives me an error 400.
public void saveOpportunityToServer (final String textToSave, String fileName)
{
try
{
StringRequest request = new StringRequest(Request.Method.PUT, "https://graph.microsoft.com/v1.0/me/drive/root:/" + fileName + ":/content",
new Response.Listener<String>() {
#Override
public void onResponse(String response)
{
Log.d ("PAST QUOTES SAVE", "Created file on server");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error)
{
Log.e ("PAST QUOTES SAVE", "Failed to create file on server: " + error.toString());
}
})
{
#Override
public byte[] getBody() throws AuthFailureError {
return textToSave.getBytes();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put ("Authorization", "Bearer " + mAccessToken);
headers.put ("Content-Type", "text/plain");
return headers;
}
};
serverRequest.getInstance().addToRequestQueue(request, "Create past quote file request");
}catch (Exception e){
Log.e ("PAST QUOTES SAVE", "Failed to create file on server");
}
}
Where serverRequest is an instance of Volley.RequestQueue.
If the function "getBody()" in String Request is left untouched, or the "textToSave" string is empty, the server responds with a success, creates the requested file, but as expected, the file is empty.
Does anyone have a solution, or at least a reason for why the plain text isn't being accepted?
Edit: Request & Response Headers and Body
Headers:
"Authorization" "Bearer [AccessToken]"
"Content-Type" "text/plain"
Body:
"This is a test"
I'm certain the authorization is working, but I guess there is a chance that the server is not properly interpreting the content-type header, however it works on the mentioned website, so I am not sure why it wouldn't in my program.
The response is an empty Error 400, which is stated as a Malformed Request by Microsoft, which leads me to believe the above, however I cant see a way to fix it.
Finally, I found where the issue is.
Due to how volley works, you cannot simply add a "Content-Type" header to the "getHeaders()" function. To change the body content type, you must override a separate function, "geyBodyContentType()"
Here is my final request code, for anyone wondering in the future how to solve this problem.
StringRequest request = new StringRequest(Request.Method.PUT, "https://graph.microsoft.com/v1.0/me/drive/root:/ " + fileName + ":/content",
new Response.Listener<String>() {
#Override
public void onResponse(String response)
{
Log.d ("PAST QUOTES SAVE", "Created file on server");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error)
{
Log.e ("PAST QUOTES SAVE", "Failed to create file on server: " + error.toString());
}
})
{
#Override
public byte[] getBody() throws AuthFailureError {
return textToSave.getBytes();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put ("Authorization", "Bearer " + mAccessToken);
return headers;
}
#Override
public String getBodyContentType ()
{
return ("text/plain");
}
};
Related
I'm trying to send a photo taken with my phone/emulator camera to a Node.js server, so I'm encoding it to make a string POST request.
I tried to encode it using other answers on the internet, with a byte array stream and a compressed version of the Bitmap. On Node.js I'm trying to decode it with a function that surely works; the problem is the encoding sent to Node.js from Java is bad.
Here is the encoding of the Bitmap:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
final String imageEncoded = Base64.encodeToString(b, Base64.DEFAULT);
And my POST request for the string looks like that (I'm using Volley for server interactions):
StringRequest postRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>()
{
#Override
public void onResponse(String response) {
// response
Log.d("RESPONSE", response);
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error) {
// error
Log.d("Error.Response", "ERROR" + error);
}
}
) {
#Override
protected Map<String, String> getParams()
{
Map<String, String> params = new HashMap<String, String>();
params.put("image", imageEncoded);
return params;
}
};
VolleySingleton.getInstance(getApplicationContext()).addToRequestQueue(postRequest);
I'm pretty sure the problem is that my encoding gets some extra % characters when sent to the server, so it can't be decoded well. I thought that in my Node.js code I had to use chunks to make a full string, like this:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
let body = [];
req.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
body = body.split('=');
// More code here
}
My body would look like
"image=iVBORw0KGgoAAAANSUhEUgAAAHgAAACgCAIAAABIaz%2FH..."
so I had to use split to get only the encoding, but still it didn't work. Is there a way to transform this string in another that is not URL encoded like that? Or to get through the POST request without being modified like that?
It looks like you are using the Volley library. If so, you should consider using the JSONObjectRequest instead of the StringRequest. It would look something like this, after you got your image into a JSON String format:
String myImage = "{image:" + imageEncoded + "}"
JSONObject obj = null;
try {
obj = new JSONObject(myImage);
} catch (JSONException e1) {
e1.printStackTrace();
}
JsonObjectRequest postRequest = new JsonObjectRequest(Request.Method.POST,url,obj,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
)} {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "application/json; charset=utf-8");
headers.put("Content-Type", "application/json; charset=utf-8");
return headers;
}
};
I was sending a request to the server with two parameters using volley request and it was working fine. Now the requirement has changed and I need to send at least one image or maximum 3 images to the server along with the other two parameters. The image must be sent as multi-part. I have following code for Getting image from gallery and storing their file paths in the list.
List<String> imagePathList = imageFilePaths;
List<MultipartBody.Part> partMap = new ArrayList<>();
for (int i = 0; i < imagePathList.size(); i++) {
Uri fileUri = Uri.parse(imagePathList.get(i));
RequestBody requestFile = RequestBody.create(
MediaType.parse(getMimeTypee(FileUtils.getFile(getContext(), fileUri).getAbsolutePath())),
FileUtils.getFile(getContext(), fileUri)
);
MultipartBody.Part body = MultipartBody.Part.createFormData("court_image[" + i + "]", FileUtils.getFile(getContext(), fileUri).getName(), requestFile);
partMap.add(body);
}
Where imageFilePaths is an ArrayList. The server will receive images like court_image[0], court_image[1] and so on, depends on how many image paths I have in ArrayList.
The volley request is here:
RequestQueue queue = Volley.newRequestQueue(getContext());
StringRequest postRequest = new StringRequest(Request.Method.POST, url1,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(mBaseAppCompatActivity, "Success", Toast.LENGTH_SHORT).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}
) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
String token = getToken();
params.put("Authorization", "Bearer " + token);
params.put("Content-Type", "multipart/form-data");
return params;
}
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("terms", "true");
params.put("phone", "phoneNo");
return params;
}
};
queue.add(postRequest);
Now the thing is as I am new to the multi-part thing, with the help I am able to get the image from gallery and storing their path in ArrayList but I don't know how to pass the multi-part data in this volley request. Please help.
For uploading the image along with some other parameters, I used volley. However, I found a wrapper of the original volley library which is easier to integrate for multi-part requests. Hence I added the following library in the build.gradle file.
dependencies {
compile 'dev.dworks.libs:volleyplus:+'
}
I removed the original volley library from my build.gradle and used the above library instead which can handle both multi-part and normal requests having similar integration technique.
Then I just had to write the following class which handles the POST request operation.
public class POSTMediasTask {
public void uploadMedia(final Context context, String filePath) {
String url = getUrlForPOSTMedia(); // This is a dummy function which returns the POST url for you
SimpleMultiPartRequest multiPartRequestWithParams = new SimpleMultiPartRequest(Request.Method.POST, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("Response", response);
// TODO: Do something on success
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO: Handle your error here
}
});
// Add the file here
multiPartRequestWithParams.addFile("file", filePath);
// Add the params here
multiPartRequestWithParams.addStringParam("terms", "SomeTerms");
multiPartRequestWithParams.addStringParam("phone", "85050055055");
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(multiPartRequestWithParams);
}
}
Now execute the task like the following.
new POSTMediasTask().uploadMedia(context, mediaPath);
You can upload one file at a time using this library. However, I could manage to upload multiple files, just by initiating multiple tasks.
My put method is working fine, but just changing the put request to Delete then its not working,, I tried even by sending its header. but still not working. I even tried Json object to set the parameter. Thanks in advance.
StringRequest stringRequest = new StringRequest(Request.Method.DELETE, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("blalala", response);
String qtyReserved1 = response.toString();
Toast.makeText(mContext, "ok" + qtyReserved1, Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(mContext, "not ok" + username + Integer.toString(inventoryId), Toast.LENGTH_SHORT).show();
}
})
{
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("screen_name", username);
params.put("inventory_id", Integer.toString(inventoryId));
params.put("pending", "true");
return params;
}
#Override
public String getBodyContentType() {
return "application/json";
}
};
MySingleton.mySingletonInstance(mContext.getApplicationContext()).addToRequestque(stringRequest);
Volley library don't send body when you using DELETE method. You can try other library. I am providing you an example by using loopj library
Add dependency in your gradle
compile 'com.loopj.android:android-async-http:1.4.9'
Request your web Api
RequestParams requestParams = new RequestParams();
requestParams.put("screen_name", "mariyam.shimaanath");
requestParams.put("inventory_id", 19);
requestParams.put("pending", true);
String url="192.168.4.31/api/canteen/cart";
new AsyncHttpClient().delete(url, requestParams, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
String rs = new String(responseBody);
// do whatever you want
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
}
});
After lots of research, thank God I'm managed to solve the problem,, Not exactly solved the problem had in Delete request in Volley. But, I had to changed the method i need to request from server side. I changed the request to post method, I know it might not be good practice and solution, but now i managed to delete from sever. thats what i need.
I am trying to pass
data={"process" : "ExampleProcess", "phone" : "123456"}
As a post request in volley, thus far I have managed to create a(very basic) method that will send
the required information out to the server, but I am getting a failed response.
I added logs to the program for which I can see
ERROR:: com.android.volley.ParseError: org.json.JSONException: End of input at character 2 of
But I believe it has to do with the way I am trying to send my information over. Upon debugging I also came to realize that I was getting an error indicating
Method threw 'java.lang.NullPointerException' exception. Cannot evaluate org.json.JSONObject.toString();
For which I do not know what to do, even if I change the:
total.put("data", data.toString());
to
total.put("data", data);
The error will persist. At this point I have no clue as to how can I go about sending the correct JSON post request to my URL(which has been taken out from the example btw)
public void volleyConnector(String url) {
final JSONObject data = new JSONObject();
final JSONObject total = new JSONObject();
try {
data.put("process", "ExampleProcess");
data.put("phone" , "123456789");
total.put("data", data.toString());
} catch(JSONException e) {
Log.v("JSON ERROR: ", e.toString());
}
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest( url, total, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.v("--------------","---------------------------");
Log.v("RESPONSE: ", "-----> " + response.toString());
Log.v("<----- BTW------>", total.toString());
Log.v("--------------","---------------------------");
}
}, new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
Log.v("--------------","---------------------------");
Log.v("RESPONSE: " , "xXxX FAIL X_____X FAIL XxXx");
Log.v("|>>>ERROR: ", error.toString());
//Log.v("|>>>ERROR: ", error.getLocalizedMessage());
Log.v("<----- BTW------>", total.toString());
Log.v("--------------","---------------------------");
} // end of onErrorREsponse
}){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json; charset=utf-8");
return headers;
}
};
Volley.newRequestQueue(this).add(jsonObjectRequest);
} // end of volley connector method
My complete error log(upon the failed request) reads:
V/RESPONSE:: xXxX FAIL X_____X FAIL XxXx
V/|>>>ERROR:: com.android.volley.ParseError: org.json.JSONException: End of i
V/<----- BTW------>: {"data":"{\"process\":\"ExampleProcess\",\"phone\":\"123456789\"}"}
I am not too sure as to what is going on and I am not getting anywhere by reading the docs and debugging with Android studio. As far as I am concerned, one of the parameters is a JSONObject and I am passing in one albeit it might be wrongly formatted.
Any help/pointers will be greatly appreciated.
You are doing correct but try code once by making one small change in your code that is-
JSONObject data= new JSONObject();
data.accumulate("username", "mobileGps");
data.accumulate("password", "9565551236");
JSONObject total= new JSONObject();
total.put("data",data);
json = jsonObjectNew.toString();
And pass this "json" to method and check once. Looking same but try once.
I am making JsonObjectRequest with Put method but it is not working and getting "{"detail":"Method \"GET\" not allowed."}" error message.
It is working fine on Postman. See attached screenshots for more information.
I didn't modify JsonObjectRequest. I copy this code from google sample code from here "http://developer.android.com/training/volley/request.html".
I don't think this could be a bug in Volley. Please go through my code and let me know what I am doing wrong.
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.PUT, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
hideDialog();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
hideDialog();
}
})
{
#Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
headers.put("Authorization", "Token " + dm.readString("auth_token"));
return headers;
}
};
AppController.getInstance().addToRequestQueue(jsObjRequest);
Edit: I know in the screenshot it shows 400 bad request. It is because i need to pass 2 params ie {"dg_id":"80","delivery_ids":["90936"]}. With this params also i am getting the same error in Volley.
Sample auth_token Value: MTIzNDU2NzIzNDM6ZGVsaXZlcnlndXk=
Sample Body value: {"dg_id":"80","delivery_ids":["90936"]}
Add "/" at end of DELETE, PUT interface url,do check out the following snippet.
If a client issues a GET request to "/testdir/" (i.e., at the
directory).......It is interesting to take note that if a client issue
a GET request to "/testdir" (without specifying the directory path
"/"), the server returns a "301 Move Permanently" with a new
"Location" of "/testdir/", as follows.