Android:尝试使用 Fabric 和 Twitter REST 向媒体发送推文时出现“400 错误请求”错误 API
Android: "400 bad request" error when trying to tweet with media using Fabric and Twitter REST API
我正在尝试构建一个应用程序,用户可以在其中 post 到 Twitter 并可以附加图像。我将 Fabric 用于 Twitter 集成和推文,我正在尝试扩展 API 客户端以制作状态服务更新方法的改进版本。我不想使用 Tweet Composer 发推文图片,因为我想避免加号 UI 调用,我还希望能够在推文中包含位置。
这是我的自定义 Twitter API 客户端 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);
}
这是我尝试发推的部分代码(目前我对媒体实体使用静态值 - 我使用 twurl 上传的图片的 ID):
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();
}
}
});
创建签名的函数:
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);
}
当我 运行 应用程序和我尝试发推文时,我收到以下错误消息:400 Bad Request
问题是什么?我应该怎么做?我是 Fabric、Twitter REST API 和 Retrofit 的新手,所以我希望得到更有经验的成员的建议 :) 提前致谢!
编辑:
我试图记录发送到服务器的请求和我得到的响应,现在我在 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)
正如我们所知,错误的请求错误可能指的是格式错误的请求(例如 JSON 的问题),但是,根据 Twitter REST API 文档和其他用户的示例,我请求应该没问题。我还尝试发送一条没有任何媒体 ID 的推文(至于那些我完全遵循 Fabric Statuses Service 的更新方法中提供的格式的其他字段,它应该有效)并且在那种情况下我也遇到了同样的错误。现在我完全迷路了。有人可以帮帮我吗?
编辑:
现在我通过自定义 Twitter API 客户端调用自定义更新方法,而不是使用 Retrofit REST 适配器,因此代码的更新部分现在如下所示:
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());
}
});
有了这个,我现在可以 post 没有图像(媒体 ID),但是当我尝试 post 使用媒体 ID 时,我收到以下错误:The validation of media ids failed.
我可以使用 twurl 成功 post 同一条推文,所以我不认为问题出在图片上。我使用 twurl 手动上传图像,并使用它返回的 ID。我总是在尝试 post 之前上传一张新图片,所以问题不在于重复的 ID。现在我显然不知道到底出了什么问题。有人可以帮助我吗?
好的,原来是我犯了一个愚蠢的错误。当我使用 twurl 发布图像时,我使用的是与我在应用程序中使用的不同的 Twitter 帐户。重要的是,上传的媒体必须来自同一用户或包括其他用户作为附加所有者(使用 additional_owners 参数)才能在推文中使用。我在twurl切换账号后一切正常
我正在尝试构建一个应用程序,用户可以在其中 post 到 Twitter 并可以附加图像。我将 Fabric 用于 Twitter 集成和推文,我正在尝试扩展 API 客户端以制作状态服务更新方法的改进版本。我不想使用 Tweet Composer 发推文图片,因为我想避免加号 UI 调用,我还希望能够在推文中包含位置。
这是我的自定义 Twitter API 客户端 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);
}
这是我尝试发推的部分代码(目前我对媒体实体使用静态值 - 我使用 twurl 上传的图片的 ID):
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();
}
}
});
创建签名的函数:
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);
}
当我 运行 应用程序和我尝试发推文时,我收到以下错误消息:400 Bad Request
问题是什么?我应该怎么做?我是 Fabric、Twitter REST API 和 Retrofit 的新手,所以我希望得到更有经验的成员的建议 :) 提前致谢!
编辑:
我试图记录发送到服务器的请求和我得到的响应,现在我在 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)
正如我们所知,错误的请求错误可能指的是格式错误的请求(例如 JSON 的问题),但是,根据 Twitter REST API 文档和其他用户的示例,我请求应该没问题。我还尝试发送一条没有任何媒体 ID 的推文(至于那些我完全遵循 Fabric Statuses Service 的更新方法中提供的格式的其他字段,它应该有效)并且在那种情况下我也遇到了同样的错误。现在我完全迷路了。有人可以帮帮我吗?
编辑:
现在我通过自定义 Twitter API 客户端调用自定义更新方法,而不是使用 Retrofit REST 适配器,因此代码的更新部分现在如下所示:
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());
}
});
有了这个,我现在可以 post 没有图像(媒体 ID),但是当我尝试 post 使用媒体 ID 时,我收到以下错误:The validation of media ids failed.
我可以使用 twurl 成功 post 同一条推文,所以我不认为问题出在图片上。我使用 twurl 手动上传图像,并使用它返回的 ID。我总是在尝试 post 之前上传一张新图片,所以问题不在于重复的 ID。现在我显然不知道到底出了什么问题。有人可以帮助我吗?
好的,原来是我犯了一个愚蠢的错误。当我使用 twurl 发布图像时,我使用的是与我在应用程序中使用的不同的 Twitter 帐户。重要的是,上传的媒体必须来自同一用户或包括其他用户作为附加所有者(使用 additional_owners 参数)才能在推文中使用。我在twurl切换账号后一切正常