Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server shows empty body when using POST addJSONObjectBody #53

Closed
paulpv opened this issue Nov 8, 2016 · 7 comments
Closed

Server shows empty body when using POST addJSONObjectBody #53

paulpv opened this issue Nov 8, 2016 · 7 comments

Comments

@paulpv
Copy link

paulpv commented Nov 8, 2016

Symptoms: When POSTing a valid JSONObject (89 characters deserialized) to an Ubuntu Apache Slim PHP (http://www.slimframework.com, https://github.com/slimphp/Slim) server, both Stetho and AnalyticsListener say that 89 bytes was sent, but the server sees only an empty/null body.
When I make what I believe to be the exact same request using Volley the server responds just fine as expected.
(I have yet to get Stetho to work w/ Volley, so I cannot confirm how identical the request is or isn't)

I believe that my code is pretty straightforward:

public String requestJSONObject(
        final String tag,
        @NonNull
        Uri uri,
        HashMap<String, String> bodyParameterMap)
{
    JSONObject jsonObjectBody;
    if (bodyParameterMap == null)
    {
        jsonObjectBody = null;
    }
    else
    {
        jsonObjectBody = new JSONObject();
        for (Entry<String, String> entry : bodyParameterMap.entrySet())
        {
            String key = entry.getKey();
            String value = entry.getValue();
            try
            {
                jsonObjectBody.put(key, value);
            }
            catch (JSONException e)
            {
                // ignore
            }
        }
    }

    ANRequest request = AndroidNetworking.post(uri.toString())
            .setTag(tag)
            .addHeaders("User-Agent", mUserAgent)
            .addHeaders("APP-ID", mAppId)
            .addHeaders("Debug", "true")
            .addJSONObjectBody(jsonObjectBody)
            .build();

    request.setAnalyticsListener(new AnalyticsListener()
    {
        @Override
        public void onReceived(long timeTakenInMillis, long bytesSent, long bytesReceived, boolean isFromCache)
        {
            Log.e(TAG, " timeTakenInMillis : " + timeTakenInMillis);
            Log.e(TAG, " bytesSent : " + bytesSent);
            Log.e(TAG, " bytesReceived : " + bytesReceived);
            Log.e(TAG, " isFromCache : " + isFromCache);
        }
    });

    request.getAsOkHttpResponseAndJSONObject(new OkHttpResponseAndJSONObjectRequestListener()
    {
        @Override
        public void onResponse(Response okHttpResponse, JSONObject response)
        {
            Log.i(TAG, "requestJSONObject(tag=" + tag + ") onResponse: response=" + response);
            //...
        }

        @Override
        public void onError(ANError anError)
        {
            Log.w(TAG, "requestJSONObject(tag=" + tag + ") onError: anError=" + Utils.toString(anError));
            //...
        }
    });

    return tag;
}

Usage:

Uri uri = Uri.parse("http://api.redacted.com")
    .buildUpon()
    .appendPath("oauth2")
    .appendPath("token")
    .build();
HashMap<String, String> postBodyParameters = new HashMap<>();
postBodyParameters.put("grant_type", "refresh_token");
postBodyParameters.put("refresh_token", refreshToken);
requestJSONObject("mytag", uri, postBodyParameters);

Stetho sniff:

Request URL:http://api.redacted.com/oauth2/token
Request Method:POST
Status Code:400 Bad Request
Request Headers:
Accept-Encoding:gzip
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:95
Content-Type:application/json; charset=utf-8
Host:api.redacted.com
APP-ID:redacted
User-Agent:Dalvik/2.1.0 (Linux; U; Android 5.1.1; Nexus 5 Build/LMY48B); com.redacted.testapp 1.0
Request Payload:
{"refresh_token":"redacted","grant_type":"refresh_token"}
Response Headers:
Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection:close
Content-Length:101
Content-Type:application/json; charset=utf-8
Date:Tue, 08 Nov 2016 22:34:26 GMT
Expires:Thu, 19 Nov 1981 08:52:00 GMT
OkHttp-Received-Millis:1478644376230
OkHttp-Sent-Millis:1478644375903
Pragma:no-cache
Server:Apache/2.4.7 (Ubuntu)
Set-Cookie:PHPSESSID=au8iauf08b2i3qnu4nbrd40ts6; path=/
X-Powered-By:PHP/5.5.9-1ubuntu4.20
Response Payload:
{"error":[{"message":"Post body is empty.","transaction_id":"redacted"}]}{"post_body":null}

(I had my web developer output to the response the post body that he read.)

Any idea why AN doesn't work, but Volley does?

@paulpv
Copy link
Author

paulpv commented Nov 8, 2016

FYI, here is my working Volley code:

mRequestQueue.add(new JsonObjectRequest(uri.toString(), jsonObjectBody, new Listener<JSONObject>()
{
    @Override
    public void onResponse(JSONObject response)
    {
        Log.i(TAG, "requestJSONObject(tag=" + tag + ") onResponse: response=" + response);
        //...
    }
}, new ErrorListener()
{
    @Override
    public void onErrorResponse(VolleyError error)
    {
        Log.w(TAG, "requestJSONObject(tag=" + tag + ") onError: error=" + error);
        //...
    }
})
{
    @Override
    public String getBodyContentType()
    {
        return "application/json; charset=utf-8";
    }

    @Override
    public Map<String, String> getHeaders()
            throws AuthFailureError
    {
        Map<String, String> headers = super.getHeaders();
        if (headers == null || headers.equals(Collections.emptyMap()))
        {
            headers = new HashMap<>();
        }

        headers.put("User-Agent", mUserAgent);
        headers.put("APP-ID", mAppId);
        headers.put("Debug", "true");

        return headers;
    }
});

@amitshekhariitbhu
Copy link
Owner

I just created the same situation to test it.

API - which require both and in JSONObject. otherwise it send error with error body as bad parameter

When I make request it is working perfectly. Still trying to figure out the problem you are facing.

        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("firstname", "Amit");
//            jsonObject.put("lastname", "Shekhar");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        AndroidNetworking.post("https://fierce-cove-29863.herokuapp.com/createAnUser")
                .addJSONObjectBody(jsonObject)
                .setTag(this)
                .setPriority(Priority.LOW)
                .build()
                .getAsOkHttpResponseAndJSONObject(new OkHttpResponseAndJSONObjectRequestListener() {
                    @Override
                    public void onResponse(Response okHttpResponse, JSONObject response) {

                    }

                    @Override
                    public void onError(ANError error) {

                    }
                });

Thanks
Amit Shekhar

@paulpv
Copy link
Author

paulpv commented Nov 9, 2016

Thanks for the response.
Can you clarify your statement?
What causes "otherwise it send error with error body as bad parameter", or is everything "it is working perfectly"?

@amitshekhariitbhu
Copy link
Owner

When I make request with correct data, it is giving me in onResponse method

 jsonObject.put("firstname", "Amit");
 jsonObject.put("lastname", "Shekhar");

When I make request with wrong data, it is giving me in onError method with errorBody as {"error":"bad parameter"}

 jsonObject.put("firstname", "Amit");
// not setting lastname.

@paulpv
Copy link
Author

paulpv commented Nov 9, 2016

That sounds to me like a server side logic choice that is probably by design.
What happens if you post a single field using Volley?
I still can't figure out why Volley works, but AN doesn't. :/

@paulpv
Copy link
Author

paulpv commented Nov 9, 2016

Today's investigation is leading me to believe that the problem I am seeing is related to the GzipRequestInterceptor.

OkHttpClient.Builder builder = new OkHttpClient.Builder();
//builder.addInterceptor(new GzipRequestInterceptor());
if (BuildConfig.DEBUG)
{
    builder.addNetworkInterceptor(new StethoInterceptor());
}
OkHttpClient httpClient = builder.build();
AndroidNetworking.initialize(applicationContext, httpClient);

When I comment out the GzipRequestInterceptor line, my server logic seems to work.
I can confirm that my server used to be set up to accept gzip encoding, but from looking at the headers in my first entry above, the server isn't responding w/ gzip, so perhaps it got disabled.
I will confirm w/ my web dev as soon as he recovers from last night's US election...

@paulpv
Copy link
Author

paulpv commented Nov 10, 2016

Yup!
The problem is with adding GzipRequestInterceptor when a server have gzip enabled.
gzip capabilities need to be tested as supported on the server before enabled.
Which begs a general question, which may end up being an okhttp question:
How should this negotiation be tested and enabled on a per-server basis?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants