

### 前提・実現したいこと

Flutterで[Twitter API v2](https://developer.twitter.com/en/docs/twitter-
api/tweets/manage-tweets/api-reference/post-tweets) を用いて，ツイートを行いたいと考えています。  
Timelineの取得など，GETでのリクエストは以下のように，正しく値を取得できているのですが、POSTでのリクエストは、「発生している問題・エラーメッセージ」に記載のように403が返ってきてしまいます。

ソースコードの処理の流れとしては、

  1. 別のファイルで事前に取得しFirebaseに保存している、access_tokenと、access_token_secretを読み込む。
  2. oauth1を使ってツイートをするための[エンドポイント](https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets)にリクエストをPOST形式で送信。  
という流れです。

### 発生している問題・エラーメッセージ

[code]

    flutter: {title: Forbidden, detail: Forbidden, type: about:blank, status: 403}
    
[/code]

### 該当のソースコード

[code]

    import 'package:flutter/material.dart';
    import 'package:oauth1/oauth1.dart' as oauth1;
    import 'package:url_launcher/url_launcher.dart';
    import 'root.dart';
    import 'package:cloud_firestore/cloud_firestore.dart';
    import 'package:firebase_core/firebase_core.dart';
    import 'dart:convert';
    
    void main() {
      runApp(MyApp());
    }
    
    /*
    void setData(String collection, Map data) {
      FirebaseFirestore.instance.collection(collection).add(data);
    }
    */
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyWidget(),
        );
      }
    }
    
    class MyWidget extends StatefulWidget {
      @override
      _MyWidgetState createState() => _MyWidgetState();
    }
    
    class _MyWidgetState extends State<MyWidget> {
      final controller = TextEditingController();
      final platform = oauth1.Platform(
        'https://api.twitter.com/oauth/request_token',
        'https://api.twitter.com/oauth/authorize',
        'https://api.twitter.com/oauth/access_token',
        oauth1.SignatureMethods.hmacSha1,
      );
      final clientCredentials = oauth1.ClientCredentials(
        '＜APIキー＞',
        '＜APIシークレットキー＞',
      );
      late final auth = oauth1.Authorization(clientCredentials, platform);
      oauth1.Credentials? tokenCredentials;
    
      @override
      void initState() {
        super.initState();
    
    
        // CallbackURLを"oob"とすることでPINでの認証とできる
        auth.requestTemporaryCredentials('oob').then((res) {
          tokenCredentials = res.credentials;
          // launch() で ログイン用URLを開く
          launch(auth.getResourceOwnerAuthorizationURI(tokenCredentials!.token));
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  // ログイン後に表示されたPINを入力
                  TextFormField(
                    controller: controller,
                  ),
                  ElevatedButton(
                    onPressed: () async {
                      // 入力されたPINを元に Access Token を取得
                      final pin = controller.text;
                      final verifier = pin;
                      final res = await auth.requestTokenCredentials(
                        tokenCredentials!,
                        verifier,
                      );
                      print('Access Token: ${res.credentials.token}');
                      print('Access Token Secret: ${res.credentials.tokenSecret}');
                      /*
                      FirebaseFirestore.instance.collection('test_collection1').add({
                        'access_token': res.credentials.token,
                        'access_token_secret': res.credentials.tokenSecret,
                      });
                       */
    
                      // 取得した Access Token を使ってAPIにリクエストできる
                      final client = oauth1.Client(
                        platform.signatureMethod,
                        clientCredentials,
                        res.credentials,
                      );
                      /*
                      final apiResponse = await client.get(
                        Uri.parse(
                            'https://api.twitter.com/1.1/statuses/home_timeline.json?count=1'),
                      );
                      print(apiResponse.body);
    
                       */
    
                      final getResponse = await client.get(
                        Uri.https(
                          'api.twitter.com',
                          '/2/tweets',
                          {'text': 'Hello World!!'},
                        ),
                      );
    
                      final getBody = jsonDecode(getResponse.body.toString());
                      print(getBody);
    
                      final postResponse = await client.post(
                        Uri.https(
                          'api.twitter.com',
                          '/2/tweets',
                          {'text': 'Hello World!!'},
                        ),
                      );
    
                      final postBody = jsonDecode(postResponse.body.toString());
                      print(postBody);
                    },
                    child: Text('認証'),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    
[/code]

[こちら](https://zenn.dev/luecy1/articles/4e6b59aebd9da15dc51f)に記載されているソースコードのgetをpostに変更したものでもリクエストを送ってみたが、以下のような返答が返ってきた。（OAuth
2.0では認証してくれないらしい。）

その他必要な情報があれば、教えてください。

### 補足情報

  * [Flutter公式ページ（Twitter APIの使い方）](https://www.flutter-study.dev/recipe/twitter)
  * Uri.httpsのパラメータの指定方法については、上記の「タイムラインを取得」を参考にしました。
  * suzukisさんに教えていただいたパラメータ指定方法で、コードを書いた結果。

[code]

    Error: Too many positional arguments: 1 allowed, but 2 found.
    Try removing the extra positional arguments.
                      final res = await client.post(
    
[/code]

  * 返ってきたメッセージ

[code]

    flutter: {errors: [{parameters: {ids: []}, message: The `ids` query parameter can not be empty}, {parameters: {text: [Hello World!!]}, message: The query parameter [text] is not one of [ids,expansions,tweet.fields,media.fields,poll.fields,place.fields,user.fields]}], title: Invalid Request, detail: One or more parameters to your request was invalid., type: https://api.twitter.com/2/problems/invalid-request}
    flutter: {errors: [{parameters: {text: [Hello World!!]}, message: The query parameter [text] is not one of [expansions,tweet.fields,media.fields,poll.fields,place.fields,user.fields]}], title: Invalid Request, detail: One or more parameters to your request was invalid., type: https://api.twitter.com/2/problems/invalid-request}
    
[/code]


CC BY-SA 4.0
2022-01-21T13:28:40.460
85846
2023-05-25T03:08:16.003
2022-12-09T04:22:27.857
14334
51017
question
ios twitter oauth flutter dart
Twitter API v2 でツイートを行う際にPOSTでリクエストが送れない。


>
[code]

>                       final res = await client.post(
>                         Uri.https(
>                           'api.twitter.com',
>                           '/2/tweets',
>                           {'text': 'テスト'},
>                         ),
>                       );
>  
[/code]

POSTリクエストのbodyにJSON文字列を指定しないといけないのにクエリパラメータで送ってます。

[code]

                      final res = await client.post(
                        Uri.https(
                          'api.twitter.com',
                          '/2/tweets',
                        ),
                        "{'text': 'テスト'}"
                      );
    
[/code]

触ったことないので正しいかどうかはわかりませんがこんな感じになるんじゃないでしょうか。

取り合えずぱっと気づいたのはここですが、もっと手前で引っかかってる可能性もあります。

  * どういうリクエストを送らないといけないのか
  * 自分のコードが実際どういうリクエストを送っているのか

を確認しながら進めるようにしましょう


CC BY-SA 4.0
2022-01-22T03:26:09.097
85852
2022-01-22T03:26:09.097
5793
85846
answer