简体   繁体   中英

Android: “400 bad request” error when trying to tweet with media using Fabric and Twitter REST API

I am trying to build an app where the user can post to Twitter with the possibility to attach images. I am using Fabric for Twitter integration and for the Tweeting I am trying to extend the API client to make an improved version of the Statuses Service's update method. I don't want to use Tweet Composer for tweeting with images, as I want to avoid the plus UI call and I also want to be able to include location in the tweets.

Here is my custom Twitter API client class:

public class MyTwitterApiClient extends TwitterApiClient {

    public MyTwitterApiClient(TwitterSession session)
    {
        super(session);
    }

    public StatusWithMediaService getStatusWithMediaService() {
        return  getService(StatusWithMediaService.class);
    }


}

public interface StatusWithMediaService {
    @FormUrlEncoded
    @POST(value="/1.1/statuses/update.json")
    void update(@Field(value="status")
                java.lang.String status,
                @Field(value="in_reply_to_status_id")
                java.lang.Long inReplyToStatusId,
                @Field(value="possibly_sensitive")
                java.lang.Boolean possiblySensitive,
                @Field(value="lat")
                java.lang.Double latitude,
                @Field(value="long")
                java.lang.Double longitude,
                @Field(value="place_id")
                java.lang.String placeId,
                @Field(value="display_cooridnates")
                java.lang.Boolean displayCoordinates,
                @Field(value="trim_user")
                java.lang.Boolean trimUser,
                @Field(value ="media_ids")
                String mediaIds,
                com.twitter.sdk.android.core.Callback<Tweet> cb);
}

And here is the part of my code where I am trying to tweet (as for now I am using a static value for the media entity - the ID of a picture I uploaded using twurl):

submit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (eventtype != null && mypos != null) {

                    final TwitterSession session = Twitter.getSessionManager().getActiveSession();

                    RestAdapter restAdapter = new RestAdapter.Builder()
                        .setEndpoint("https://api.twitter.com")
                        .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("testapp retrofit"))
                        .setRequestInterceptor(new RequestInterceptor() {
                    @Override
                    public void intercept(RequestFacade request) {

                        request.addHeader("Authorization", createSignature(session));

                    }
                })
                .build();

                StatusWithMediaService mediaService = restAdapter.create(StatusWithMediaService.class);

                    mediaService.update("@xxxx " + eventtype + ": \n" + comment.getText().toString(), null, false, mypos.latitude, mypos.longitude, null, true, false, "628524774898180096", new Callback<Tweet>() {
                        @Override
                        public void success(Result<Tweet> tweetResult) {
                            Toast.makeText(getActivity(), "Raportin lähettäminen onnistui.", Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void failure(TwitterException e) {
                            Log.e("testapp", e.getMessage());
                        }
                    });
                }
                else
                {
                    Toast.makeText(getActivity(), "Puuttuvat tiedot: sijainti", Toast.LENGTH_SHORT).show();
                }
            }
        });

The function for creating the signature:

public String createSignature(TwitterSession session)
    {
        byte[] b = new byte[32];
        new Random().nextBytes(b);
        String randomBytes = null;

        try
        {
            randomBytes = URLEncoder.encode(String.valueOf(b), "UTF-8");
        }
        catch (UnsupportedEncodingException e)
        {
            Log.e("encoding error", e.getMessage());
        }

        long currentTimeInMillis = System.currentTimeMillis();
        TwitterAuthToken authToken = session.getAuthToken();
        String token = session.getAuthToken().token;
        String signature = String.format("oauth_consumer_key=%s&oauth_nonce=%s&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%s&oauth_token=%s&oauth_version=1.0", MainActivity.TWITTER_KEY, randomBytes, currentTimeInMillis, token);
        String finalSignature = null;
        try
        {
            finalSignature = sha1(signature, MainActivity.TWITTER_SECRET + "&" +authToken.secret);
        }
        catch (UnsupportedEncodingException e)
        {
            Log.e("encoding error", e.getMessage());
        }
        catch (NoSuchAlgorithmException e)
        {
            Log.e("algorithm error", e.getMessage());
        }
        catch (InvalidKeyException e)
        {
            Log.e("key error", e.getMessage());
        }

        String header = String.format("OAuth oauth_consumer_key=\"%s\", oauth_nonce=\"%s\", oauth_signature=\"%s\", " +
                "oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"%s\", oauth_token=\"%s\", oauth_version=\"1.0\")", MainActivity.TWITTER_KEY, randomBytes, finalSignature, currentTimeInMillis, token);

        return header;
    }

public static String sha1(String s, String keyString) throws
            UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {

        SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");

        mac.init(key);
        byte[] bytes = mac.doFinal(s.getBytes("UTF-8"));

        return Base64.encodeToString(bytes, Base64.URL_SAFE);
    }

When I run the app and I try to tweet, I get the following error message: 400 Bad Request

What is the problem? What should I do differently? I am quite new to Fabric, Twitter REST API and Retrofit, so I would appreciate advice from more experienced members :) Thanks in advance!

EDIT:

I tried to log the request sent to the server and the response I get, and now I get this in logcat:

08-05 10:46:54.364  20026-20561/com.example.test.testapp D/testapp retrofit﹕ ---HTTP POST https://api.twitter.com/1.1/statuses/update.json
08-05 10:46:54.364  20026-20561/com.example.test.testapp D/testapp retrofit﹕ Authorization: OAuth oauth_consumer_key="XXXXX", oauth_nonce="XXXXX", oauth_signature="XXXXX", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1438760814359", oauth_token="XXXXX", oauth_version="1.0")
08-05 10:46:54.364  20026-20561/com.example.test.testapp D/testapp retrofit﹕ Content-Type: application/x-www-form-urlencoded; charset=UTF-8
08-05 10:46:54.364  20026-20561/com.example.test.testapp D/testapp retrofit﹕ Content-Length: 164
08-05 10:46:54.366  20026-20561/com.example.test.testapp D/testapp retrofit﹕ status=%40tkl_testi+Onnettomuus%3A+%0A&possibly_sensitive=false&lat=61.4892668&long=23.7791515&display_cooridnates=true&trim_user=false&media_ids=628825606512316416
08-05 10:46:54.366  20026-20561/com.example.test.testapp D/testapp retrofit﹕ ---> END HTTP (164-byte body)
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ <--- HTTP 400 https://api.twitter.com/1.1/statuses/update.json (803ms)
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ : HTTP/1.0 400 Bad Request
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ content-length: 0
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ date: Wed, 05 Aug 2015 07:46:55 GMT
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ server: tsa_b
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ X-Android-Received-Millis: 1438760815133
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ X-Android-Response-Source: NETWORK 400
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ X-Android-Sent-Millis: 1438760814969
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ x-connection-hash: fb74c0c2ee1c7830163453a33dd099de
08-05 10:46:55.170  20026-20561/com.example.test.testapp D/testapp retrofit﹕ <--- END HTTP (0-byte body)

As we know, the bad request error could refer to malformed request (so eg problems with the JSON), however, according to the Twitter REST API documentation and other users' examples my request should be fine. I also tried sending a tweet without any media ID (as for those other fields I completely followed the format provided in Fabric Statuses Service's update method, it should have worked) and I got the same error in that case too. Now I am completely lost. Could someone help me, please?

EDIT:

Instead of using the Retrofit REST adapter, now I call my custom update method through my custom Twitter API client, so the update part of the code now looks like this:

MyTwitterApiClient myclient = new MyTwitterApiClient(Twitter.getSessionManager().getActiveSession());

                    StatusWithMediaService mediaService = myclient.getStatusWithMediaService();
                    mediaService.update("@xxxx " + eventtype + ": \n" + comment.getText().toString(), null, false, mypos.latitude, mypos.longitude, null, true, false, "629232743633842180", new Callback<Tweet>() {
                        @Override
                        public void success(Result<Tweet> tweetResult) {
                            Toast.makeText(getActivity(), "Raportin lähettäminen onnistui.", Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void failure(TwitterException e) {
                            TwitterApiException apiException = (TwitterApiException) e;
                            Log.e("testapp", apiException.getErrorMessage());
                            Log.e("testapp", apiException.getMessage());
                        }
                    });

With this I can now post without images (media IDs), but when I try to post with a media ID, I get the following error: The validation of media ids failed.

I can successfully post the same tweet using twurl, so I don't think that the problem is with the image. I upload the images manually using twurl and I use the ID returned by it. I always upload a new image before attempting to post, so the problem is not with duplicate IDs. Now I clearly have no idea what is actually going wrong. Can someone help me?

OK, so it turned out it was due to a stupid mistake from my part. When I posted the image with twurl, I did it under a different Twitter account from the one I was using in the app. It is important that the uploaded media has to either come from the same user or include the other user as additional owner (with the additional_owners parameter) in order to be used in a tweet. After I switched accounts in twurl, everything worked fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM