Dart/Flutter - 如何获得纪元以来的第二个:Twitter API 错误代码 135(时间戳超出范围)

Dart/Flutter - How to get second since epoch : Twitter API error code 135 ( Timestamp out of bounds)

你好,希望你一切都好。 最近,我在使用 Twitter API 时恰恰在发布推文时遇到问题。 根据文档 here,我的请求必须在其 header 中包含一个字段 oauth_timestamp,这是自纪元以来的秒数(seconds since Unix 时代)。

使用 Dart(Flutter),我检索自纪元以来经过的毫秒数,然后除以 1000 以获得该值(以秒为单位)。 例如:

final oAuthTimestamp =
        Uri.encodeComponent('${DateTime.now().millisecondsSinceEpoch / 1000}');

The values must be encoded in percentage encoding, that's why I put the whole date in the Uri.encodeComponent() method.

但奇怪的是 Twitter API 向我发送了一个错误,告诉我我的时间戳超出范围,我不明白为什么会出现此错误。

{"errors":[{"code":135,"message":"Timestamp out of bounds."}]}

你能帮我解决这个错误吗?并告诉我我犯的错误。

谢谢。

下面是我使用 Twitter API

发布推文的方法
Future<void> tweet() async {
    final httpMethod = 'POST';
    final baseURL = 'https://api.twitter.com/1.1/statuses/update.json';
    final status =
        Uri.encodeComponent('Hello this is my first Tweet using Twitter API');
    final includeEntities = Uri.encodeComponent('true');
    final oAuthConsumerKey = Uri.encodeComponent('my_consumer_key');  //hidded for security purpose
    final consumerSecretKey = Uri.encodeComponent('my_consumer_secret_key');  //hidded for security purpose
    final oAuthNonce = Uri.encodeComponent(generateRandomString());  //METHOD TO GENERATE RANDOM STRING ATTACHED BELOW
    final oAuthSignatureMethod = Uri.encodeComponent('HMAC-SHA1');
    final oAuthTimestamp =
        Uri.encodeComponent('${DateTime.now().millisecondsSinceEpoch / 1000}');

    final accessToken = await _twitterAuthService.accessToken; //GETTING MY CURRENT USER ACCESS TOKEN
    final secretToken = await _twitterAuthService.secretToken; //GETTING MY CURRENT USER SECRET TOKEN
    final oAuthSecretToken = Uri.encodeComponent(secretToken);
    final oAuthToken = Uri.encodeComponent(accessToken);

    final oAuthVersion = Uri.encodeComponent('1.0');

    //THIS MY PERCENTAGE ENCODED FIELDS KEY NAME 
    final includeEntitiesField = Uri.encodeComponent('include_entities');
    final oAuthConsumerKeyField = Uri.encodeComponent('oauth_consumer_key');
    final oAuthNonceField = Uri.encodeComponent('oauth_nonce');
    final oAuthSignatureField = Uri.encodeComponent('oauth_signature');
    final oAuthSignatureMethodField =
        Uri.encodeComponent('oauth_signature_method');
    final oAuthTimestampField = Uri.encodeComponent('oauth_timestamp');
    final oAuthTokenField = Uri.encodeComponent('oauth_token');
    final oAuthVersionField = Uri.encodeComponent('oauth_version');
    final statusField = Uri.encodeComponent('status');

    final parameterString = Uri.encodeComponent(
        '$includeEntitiesField=$includeEntities&$oAuthConsumerKeyField=$oAuthConsumerKey&$oAuthNonceField=$oAuthNonce&$oAuthSignatureMethodField=$oAuthSignatureMethod&$oAuthTimestampField=$oAuthTimestamp&$oAuthTokenField=$oAuthToken&$oAuthVersionField=$oAuthVersion&$statusField=$status');
    final encodedBaseUrl = Uri.encodeComponent(baseURL);

    final signatureBaseString = '$httpMethod&$encodedBaseUrl&$parameterString';

    final signingKey = '$consumerSecretKey&$oAuthSecretToken';

    final hmacSCHA1 = Hmac(sha1, convert.utf8.encode(signingKey))
        .convert(convert.utf8.encode(signatureBaseString));

    final oAuthSignature =
        Uri.encodeComponent(convert.base64.encode(hmacSCHA1.bytes));

    final finalUrl = '$baseURL?$statusField=$status';
    final headerString =
        'OAuth $oAuthConsumerKeyField="$oAuthConsumerKey", $oAuthNonceField="$oAuthNonce", $oAuthSignatureField=$oAuthSignature, $oAuthSignatureMethodField=$oAuthSignatureMethod, $oAuthTimestampField=$oAuthTimestamp, $oAuthTokenField=$oAuthToken, $oAuthVersionField=$oAuthVersion';
    
    final response = await _client.post(finalUrl,
        headers: {Fields.authorization.toLowerCase(): headerString});

    print('REQUEST RESPONSE. \nbody = ${response.body}');
  }

这是我用来生成 oauth_nonce:

的方法
  String generateRandomString() {
    final random = Random();
    final chars =
        'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';

    final generatedString = String.fromCharCodes(
      Iterable.generate(
        42,
        (_) {
          final generatedChars = chars.codeUnitAt(random.nextInt(chars.length));
          return generatedChars;
        },
      ),
    );

    return generatedString;
  }

有用link:Twitter API documentation to POST a Tweet and Create a oauth_signature

P.S:我的 unsing cypto 用于 hmac-sha1 加密

我解决了阻止我使用 Twitter API 发布推文的问题。

首先: 我保留了检索自纪元以来经过的时间(以秒为单位)的方式

final oAuthTimestamp = Uri.encodeComponent('${DateTime.now().millisecondsSinceEpoch / 1000}');

最后是:

我从 Twitter API 收到一条错误消息告诉我 {"errors":[{"code":32,"message":"Could not authenticate you."}]}

我只是在创建 parameterString

时删除了 include_entities 字段

之前

final parameterString = Uri.encodeComponent(
        '$includeEntitiesField=$includeEntities&$oAuthConsumerKeyField=$oAuthConsumerKey&$oAuthNonceField=$oAuthNonce&$oAuthSignatureMethodField=$oAuthSignatureMethod&$oAuthTimestampField=$oAuthTimestamp&$oAuthTokenField=$oAuthToken&$oAuthVersionField=$oAuthVersion&$statusField=$status');

之后

 final parameterString = Uri.encodeComponent(
        '$oAuthConsumerKeyField=$oAuthConsumerKey&$oAuthNonceField=$oAuthNonce&$oAuthSignatureMethodField=$oAuthSignatureMethod&$oAuthTimestampField=$oAuthTimestamp&$oAuthTokenField=$oAuthToken&$oAuthVersionField=$oAuthVersion&$statusField=$status');

老实说,我不知道为什么这个 include_entities 字段会在身份验证期间产生错误。但是在文档中, include_entities=true 包含在 creating a signature (oauth_signature 的步骤中)

所以我凭直觉删除了这个字段,因为我不明白它的作用。然后一切开始正常工作。 Twitter 开发人员应该查看他们的文档并更明确地说明此 include_entities 字段的用途。

谢谢你所做的一切