Android - Volley post parameters adding extra '&' - android

I am converting my application to use the Volley framework.
I have everything working as far as I can tell, when I make a webservice request using HttpClient, wireshark is showing my parameters as
devicePrint=Android&mobileSaveUserId=false&__ResponseType=XML&appVersion=Android-3.2&__ResponseTypeVersion=1.0&userId=mbltest6&retrievePostedTransaction=falseHTTP/1.1
200 OK
This works perfectly, but when I use volley, my packet looks like this.
devicePrint=Android&mobileSaveUserId=false&__ResponseType=XML&appVersion=Android-3.2&__ResponseTypeVersion=1.0&userid=mbltest6&retrievePostedTransaction=false&HTTP/1.1
200 OK
I moved them side by side and noticed the volley example (The bottom one) is appending an extra "&" which is causing my webservice to return "Username not found" even though it is clearly in there.
Any suggestions on how to fix this? Or why it's happening?
EDIT: Upon further investigation into the base classes, when you set the parameters it always adds an extra "&" to the end of the string. Is this normal behavior for volley? Could this be whats causing my request to fail?
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
Iterator uee = params.entrySet().iterator();
while(uee.hasNext()) {
java.util.Map.Entry entry = (java.util.Map.Entry)uee.next();
encodedParams.append(URLEncoder.encode((String)entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode((String)entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException var6) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, var6);
}
}
My code is as follows:
public abstract class WebServiceRequest extends StringRequest {
private static final int SOCKET_TIMEOUT = (1000 * 60);
private static final String RESPONSE_TYPE_KEY = "__ResponseType";
private static final String RESPONSE_TYPE_VERSION_KEY = "__ResponseTypeVersion";
private static final String APP_VERSION_KEY = "appVersion";
private static final String RETRIEVE_POSTED_KEY = "retrievePostedTransaction";
public boolean mRetrievePostedTransactions;
private Map<String, String> mParameters;
public WebServiceRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, listener, errorListener);
this.mParameters = new HashMap<>();
setRetryPolicy(new DefaultRetryPolicy(
SOCKET_TIMEOUT,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
));
}
public abstract void setParams(Map<String,String> params);
#Override
protected Map<String, String> getParams() throws AuthFailureError {
setCommonParameters(mParameters);
setParams(mParameters);
return mParameters;
}
private Map<String, String> setCommonParameters (Map<String,String> params) {
params.put( APP_VERSION_KEY, "Android-" + VirtualWalletApplication.getInstance().appVersion );
params.put( RESPONSE_TYPE_KEY, "XML" );
params.put( RESPONSE_TYPE_VERSION_KEY, "1.0" );
params.put( RETRIEVE_POSTED_KEY, Boolean.valueOf( mRetrievePostedTransactions ).toString() );
return params;
}
}
Here is where i create my request
WebServiceRequest request = new WebServiceRequest(
StringUtils.getFullServerUrlForResource("/alservlet/ValidateUserIdServlet"),
onSuccessListener,
onFailureListener) {
#Override
public void setParams(Map<String, String> params) {
params.put("userid","mbltest6");
params.put("mobileSaveUserId","false");
params.put("devicePrint","Android");
}
};
NetworkVolley.getInstance().sendRequest(request);

Turns out, i needed to capitalize the parameter "userId" not "userid". Keep a close eye on your parameters people!

Related

How to post audio and image as multipart/formdata in native android?

I want to post Form Data like this,
where,
ApiKey, userid, albumid, music_name, singer_name are the keys whose corresponding values are all text type.
music_cover and music_file are the keys for image file and audio file as their value.
All the values are non nullable. That is, must pass all values to the
server to get a success response.
So, all in all, I have a bunch of texts and an audio and an image to upload to server using web service from android.
I am picking the image and audio using picker, so I have their file path.
Please guide me through the process of uploading audio and image using multipart from android.
It has kept me up all night and yet no reprieve.
Here I created an example using Volley
So first of all we have to build a RestApiMultiPartRequests.class so here i created it like this
private class RestApiMultiPartRequests extends Request {
private final Map<String, String> mStringParts;
private final Map<String, File> mFileParts;
private MultipartEntityBuilder mBuilder;
private final Response.Listener<T> mListener;
public RestApiMultiPartRequests(String url,
Map<String, String> stringParts,
Map<String, File> fileParts,
Response.Listener<T> listener,
Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
mListener = listener;
mStringParts = stringParts;
mFileParts = fileParts;
buildMultipartEntity();
}
private void buildMultipartEntity() {
if (mBuilder != null) {
mBuilder = null;
}
mBuilder = MultipartEntityBuilder.create();
mBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
mBuilder.setBoundary("_____" + Long.toString(System.currentTimeMillis()) + "_____");
mBuilder.setCharset(Consts.UTF_8);
if (mStringParts != null) {
for (Map.Entry<String, String> entry : mStringParts.entrySet()) {
mBuilder.addTextBody(entry.getKey(), entry.getValue(), ContentType.create("text/plain", Charset.forName("UTF-8")));
}
}
Log.e("Size", "Size: " + mFileParts.size());
for (Map.Entry<String, File> entry : mFileParts.entrySet()) {
ContentType imageContentType = ContentType.create("image/*");//MULTIPART_FORM_DATA;
Log.d("", "Key " + entry.getKey());
Log.d("", "Value " + entry.getValue());
Log.d("", "Name " + entry.getValue().getName());
//"userfile"
mBuilder.addBinaryBody(entry.getKey(), entry.getValue(), imageContentType, entry.getValue().getName());
}
}
#Override
public String getBodyContentType() {
return mBuilder.build().getContentType().getValue();
}
#Override
public byte[] getBody() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
mBuilder.build().writeTo(bos);
} catch (IOException e) {
e.printStackTrace();
}
return bos.toByteArray();
}
public HttpEntity getEntity() {
return mBuilder.build();
}
#SuppressWarnings("unchecked")
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return (Response<T>) Response.success(jsonString, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
}
Using this class we can build a request like this
private void UploadImage() {
ServiceCall.RestApiMultiPartRequests<String> restApiMultiPartRequest =
new ServiceCall.RestApiMultiPartRequests<String>(url/*YOUR SERVICE URL*/, hashMap /* HASHMAP OF STRING */, fileparts /*HASH MAP OF FILE AND STRING */, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
/* HANDEL YOUR SUCCESS RESPONSE **/
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// Handle your error types accordingly.For Timeout & No
// connection error, you can show 'retry' button.
// For AuthFailure, you can re login with user
// credentials.
// For ClientError, 400 & 401, Errors happening on
// client side when sending api request.
// In this case you can check how client is forming the
// api and debug accordingly.
// For ServerError 5xx, you can do retry or handle
// accordingly.
/** HANDLE YOUR ERRORS */
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Authorization","YOUR AUTHANTICATION TOKEN IF REQUIRED");
return params;
}
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
return params;
}
};
restApiMultiPartRequest.setRetryPolicy(new DefaultRetryPolicy(0, 1, 2));//10000
VOLLEY_INSTANCE.addToRequestQueue(restApiMultiPartRequest);
}
Here hashmap is HashMap<String, String> hashMap
and fileparts is HashMap<String, File> fileparts;
so the parameters with String key and String value add in to hashmap
and parameters with String key and File Value add into fileparts

LineBreaker in Response not display Properly

I have a problem with displaying Line Breaker in return response. When I check that text in different devices I got different symbols.
I have put way of set the response using gson & requested using Volley..
......
Gson gson=new Gson();
ItemsHolder = gson.fromJson(response.toString(),
ItemsHolder.class);
.......
......
MenuItemsConfigurationListner configurationListner = new MenuItemsConfigurationListner();
AuthonicateRequest jsonObjectRequest = new AuthonicateRequest(
Request.Method.GET,
url,
(String) null,
configurationListner,
configurationListner
);
jsonObjectRequest.setTag(TAG);
VolleySingleton.getVolleySingletonInstance().addToRequestQueue(
jsonObjectRequest);
...............
Headers
private class AuthonicateRequest extends JsonObjectRequest {
public AuthonicateRequest(int method, String url, String requestBody, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
super(method, url, requestBody, listener, errorListener);
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return createBasicAuthHeader();
}
Map<String, String> createBasicAuthHeader() {
Map<String, String> headerMap = new HashMap<String, String>();
headerMap.put(IConstants.CONTENT_TYPE, IConstants.APPLICATION_JSON);
return headerMap;
}
}
If response return from the server contains the line breaker this is the way of removing that and it replace with '\n' android studio supporting line breaker.
private class MyListener implements Response.Listener<JSONObject>, Response.ErrorListener {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(getTag() , error.toString());
}
#Override
public void onResponse(JSONObject response) {
MyItem item = new Gson().fromGson(response.toString(),MyItem.class);
String desc = null != item.getContent() ? item.getContent() : getEmptyString()
desc = desc.replace("\u2028", System.getProperty("line.separator"));
textViewItemDesc.setText(desc`enter code here`);
}
}
If you are setting the text in a TextView, you can use
Html.fromHtml(yourText);
It converts basic html tags to corresponding Span and you can use the resulting Spanned string in your TextView.
textView.setText(Html.fromHtml(yourText));

Android volley DefaultRetryPolicy do not work as intended

So, I have this Volley PUT request:
private boolean syncCall(JSONObject jsonObject, final VolleyCallback
callback) {
final ProgressDialog progDailog = new ProgressDialog(context);
final Boolean[] success = {false};
progDailog.setMessage("...");
progDailog.setIndeterminate(false);
progDailog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progDailog.setCancelable(false);
progDailog.show();
final SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
RequestQueue queue = Volley.newRequestQueue(context, new HurlStack());
final String token = prefs.getString("token", null);
String URL = Constants.getUrlSync();
String param1 = String.valueOf(prefs.getInt("pmp", 1));
String param2 = String.valueOf(prefs.getInt("ei", 1));
URL = URL.replace("[x]", param1);
URL = URL.replace("[y]", param2);
//pegar id pmp e IE corretas
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request
.Method.PUT, URL, jsonObject,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
callback.onSuccess(response + "");
success[0] = true;
progDailog.dismiss();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.onFailure(error);
tokenFailure(error);
success[0] = false;
progDailog.dismiss();
}
}) {
#Override
public Map<String, String> getHeaders() throws
AuthFailureError {
HashMap<String, String> headers = new HashMap<>();
headers.put("Token", token);
return headers;
}
};
int socketTimeout = 30000;
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsObjRequest.setRetryPolicy(policy);
queue.add(jsObjRequest);
return success[0];
}
My problem is that I send a very large JSON, so the default timeout of 5 seconds is not enough. So, I tried to increase the timeout to 30 seconds and messing with the DefaultRetryPolicy to increase the number of retries.
The thing is, it keeps timeouting in 5s and it doesn't even retry once!
Do I have to have a listener or a callback for the retries ? I'm doing something wrong with the DefaultRetryPolicy ? Please help, this issue is driving me nuts...
Do you need to use the DefaultRetryPolicy?
Because you could define your own.
Instead of this:
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
Try this:
jsObjRequest.setRetryPolicy(new RetryPolicy() {
#Override
public int getCurrentTimeout() {
// Here goes the new timeout
return mySeconds;
}
#Override
public int getCurrentRetryCount() {
// The max number of attempts
return myAttempts;
}
#Override
public void retry(VolleyError error) throws VolleyError {
// Here you could check if the retry count has gotten
// To the max number, and if so, send a VolleyError msg
// or something
}
});
I'm not sure exactly why the retry time doesn't work on your code, I did find a similar issue here though.
Instead, I can tell you some things that I don't believe to be ok in your code and suggest you to adopt my model of Volley use.
First of all, you're creating a new request queue for every request you're making. That's not cool, you should have a RequestManager singleton that holds one request queue and use that.
Second of all, I don't know if this is what affects the retry time, I have a base request class and set the retry time in the constructor. Then, I extend this class whenever I have to implement a new type of request. Then, I create an instance of the request, set the callbacks, and pass it to the request manager. The request manager adds it to the one request queue I was talking about.
More over, if you don't already, I suggest you use the Gson library to parse the JSON objects.
This is my base request class I'm using:
/**
* Created by Daniel on 2/6/2016.
*/
public class GsonRequest<T> extends Request<T> {
protected Context context;
protected final Gson gson = new Gson();
protected final Class<T> clazz;
protected final TypeToken typeToken;
protected Map<String, String> headers;
protected Map<String, String> params;
protected final Response.Listener<T> listener;
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url URL of the request to make
* #param clazz Relevant class object, for Gson's reflection
*/
public GsonRequest(final Context context, final int requestMethod, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(requestMethod, url, errorListener);
this.context = context;
this.clazz = clazz;
this.listener = listener;
this.headers = new HashMap<>();
typeToken = null;
setRetryPolicy();
}
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url URL of the request to make
* #param typeToken Relevant typeToken object, for Gson's reflection
*/
public GsonRequest(final Context context, final int requestMethod, String url, TypeToken typeToken, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(requestMethod, url, errorListener);
this.context = context;
this.typeToken = typeToken;
this.listener = listener;
this.headers = new HashMap<>();
clazz = null;
setRetryPolicy();
}
#Override
protected Map<String, String> getParams() throws AuthFailureError {
return params != null ? params : super.getParams();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
//TODO add headers here
return headers;
}
#Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
JSONObject jsonObject = new JSONObject(json);
if (clazz != null) {
return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
} else {
return Response.success((T) gson.fromJson(json, typeToken.getType()), HttpHeaderParser.parseCacheHeaders(response));
}
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
} catch (JSONException e) {
return Response.error(new ParseError(e));
}
}
protected void setRetryPolicy() {
//TODO set your retry policy here
setRetryPolicy(new DefaultRetryPolicy(
30000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
}
}`enter code here`
This works like a charm for me. Hope it helps, if you need any further help, contact me

how to execute PUT request in Android Volley?

I am currently using GsonRequest to issue rest GET requests. Not clear on what to use for PUT requests where I need to send over a whole JSon object to be updated. The Request object will accept PUT but I'm not sure how place the JSon object that is expected.
Here is my json to be PUT:
{
prop1 : true,
prop2 : false,
prop4 : true
}
Here is how its submitted in apiary.io for example:
var xhr = new XMLHttpRequest();
xhr.open('PUT', 'http://my.apiary.io/v1/records/{myid}.json');
xhr.send("{\n \"isEditable\": false,\n \"isClosed\": true,\n \"isAvail\": true\n}");
I don't know where to put the JSON.
Thanks
public class GsonRequest<T> extends Request<T> {
private final Gson gson ;
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;
public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener) {
super(method, url, errorListener);
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Timestamp.class, new TimestampDeserializer());
this.gson = gsonBuilder.create();
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
#Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(
gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
}
And here are the base getBody methods inside the request. It seems to handle parameters on the Method.PUT, but what if its a JSON string that needs to be sent in the body?
/**
* Returns the raw POST or PUT body to be sent.
*
* #throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
*/
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
Suggested solution:
// add a Json body.
public String jsonBody;
/**
* Returns the raw POST or PUT body to be sent.
*
* #throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
if ((getMethod() == Method.PUT) && (jsonBody != null))
{
return jsonBody.getBytes(); // Encoding required?????
}
else
{
return super.getBody();
}
}
The abstract base class Request has a constructor which takes a Request.Method as the first parameter. All the Request implementations in volley.toolbox also have a constructor like that as well.
I'm not sure where GsonRequest is coming from but if it doesn't have a constructor which takes a Method, you can add one yourself.
Edit: You can override getBody and getBodyContentType to return the custom request body and MIME type respectively.
I know this is a quite old topic but i had to adapt the suggested solution to make it work for me, so i though the same approach could work for others:
I had to add some headers to the getHeaders function:
/**
* Passing some request headers
* */
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
headers.put("Accept", "*/*");
return headers;
}
Then i was able to use the suggested solution with getBody()
#Override
/**
* Returns the raw POST or PUT body to be sent.
*
* #throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
String jsonBody = "{\"status\":\"true"}";
if ((getMethod() == Method.PUT) && (jsonBody != null)){
return jsonBody.getBytes();
}else{
return super.getBody();
}
}

How does one use Basic Authentication with Volley on Android?

I'm looking through examples and code but I don't see anything implemented. Is this possible at this stage?
For those who don't want to use Spring for Android just for that, here's how to do it.
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String creds = String.format("%s:%s","USERNAME","PASSWORD");
String auth = "Basic " + Base64.encodeToString(creds.getBytes(), Base64.DEFAULT);
params.put("Authorization", auth);
return params;
}
Note that you may have to use Base64.NO_WRAP instead of Base64.DEFAULT for this to work. As pointed in the comments.
API 8+
Yes it's possible. You need to override Request.getHeaders(). I'm lazy and I used HttpHeaders and HttpAuthentication from Spring for Android but you can just build the auth header and return it from the method. From getHeaders() you can return the auth header for basic auth. This is a sample request with basic auth.
public class GetUser extends Request<User> {
private static final String TAG = GetUser.class.getName();
private Response.Listener<User> mListener;
private ObjectMapper mMapper = new ObjectMapper();
public GetUser(Response.ErrorListener errorListener, Response.Listener<User> listener){
super(Method.GET, PoisUtils.BASE_URL + "/users", errorListener);
mListener = listener;
}
#Override
protected Response<User> parseNetworkResponse(NetworkResponse response) {
String jsonString = new String(response.data);
try {
User result = mMapper.readValue(jsonString, User.class);
return Response.success(result, getCacheEntry());
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
return null;
}
#Override
protected void deliverResponse(User response) {
mListener.onResponse(response);
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return AuthUtils.buildAuthHeaders().toSingleValueMap();
}
}
And here is how I build the auth headers
public static HttpHeaders buildAuthHeaders(){
if(UserUtils.isUserLogged()){
HttpHeaders requestHeaders = new HttpHeaders();
User user = PoisApplication.get().getUser();
HttpAuthentication auth = new HttpBasicAuthentication(
user.getUsername(), user.getPassword());
requestHeaders.setAuthorization(auth);
return requestHeaders;
}
return null;
}
For a proxy authorization (like squid) use this header :
String credentials = proxyUsername + ":" + proxyPassword;
String auth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
headers.put("Proxy-Authorization", auth);

Categories

Resources