1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Gmail APIを理解する:MessageとMessagePartの構造

Posted at

はじめに

開発でGmail APIを利用してメールの内容を取得したいということになり、Messageという構造体の取り扱いに苦戦したので、備忘録を兼ねて対処法を解説します。

目次

  1. Message構造体とMessagePart構造体
  2. コード例(Dart)
  3. 参考文献

Message構造体とMessagePart構造体

listメソッドとMessage構造体

Message構造体は、メールの一覧を取得する
GET https://gmail.googleapis.com/gmail/v1/users/{userId}/messages
というAPIのResponse bodyの主要な構成要素です。

まず、公式のマニュアルによると、Message構造体は以下の要素からなります。

listメソッドのマニュアルに説明がありますが、このAPIではMessage構造体のうちidthreadIdだけが埋められた状態で返ってくるので注意が必要です。つまり、本文の取得には別のAPIを使うことになります。それが次で説明するgetメソッドです。

getメソッドは
GET https://gmail.googleapis.com/gmail/v1/users/{userId}/messages/{id}
というAPIで利用できます。指定したidMessage構造体がすべて埋められて返ってきます。これで本文を取得することができます。

本文はpayloadというキーで取り出せます。ただし、MessagePartという別の構造体を扱うことになります。

getメソッドとMessagePart構造体

MessagePart構造体の構成について、再びマニュアルを参照します。

マニュアルの日本語訳が甘いので注意が必要です。「コンテナMIMEメッセージの部分」や「MIMEメッセージの部分タイプ」は、英語マニュアルの'message part'を指しています。

MIMEタイプというのは、文書や添付ファイルの形式を統一的に表すための記法です。本文と添付ファイルが一緒になって(一つのメールとして)送られるような電子メールを念頭において、MIMEタイプは「マルチパートタイプ」という概念を持っています。つまり、最上位にはMIMEタイプとしてmultipartというタイプをもつ外枠だけがあり、その内側にtext(本文)やvideo(動画ファイル)といったタイプをもつ個別の構成要素(それがこのマニュアルで言うところの'message part'です)があるということです。

以上の知識を前提にすると、bodyフィールドの説明にある「コンテナMIMEメッセージ部分」というのが、上で説明した「外枠」にあたるものだとわかります。つまり、本文と添付ファイルがあるようなメールを考えると、getメソッドで取得したMessagePart構造体のbodyを単にとるだけでは、メールの本文は取得できません。ここでのMessagePart構造体は「外枠」であり、そのbodyは空になっているからです。この場合、まずpartsフィールドの値(これはMessagePart構造体のリストになっています)を取得し、そのリストの各MessagePart構造体についてbodyの中身を調べる必要がある、ということです。

コード例(Dart)

以上の内容を踏まえて、APIを用いてメールの本文を取り出すコードを実装してみます。

Dartにはgoogle_sign_inというパッケージがあるので、それを使います。パッケージの使い方の説明は省きます(適宜リンクを参照してください)。

この実装では、外枠のMessagePart構造体のpartsフィールドが空であるかどうかを調べて、メールが複数の要素に別れている場合(添付ファイルを持つ場合など)にも適切に本文を取り出せるようにしています。単純にpayload['body']['data]だけではうまくいきません(理由は上で説明したとおりです)。

Future<List<dynamic>> _fetchEmails() async {
    final maxResults = 1; //取得するメールの数
    final headers = await _googleSignIn.currentUser!.authHeaders;
    final response = await http.get(
      Uri.parse(
          'https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=$maxResults'),
      headers: headers,
    );
    if (response.statusCode == 200) {
      var listRawTexts = [];
      final jsonData = jsonDecode(response.body);
      final messages = jsonData['messages'];
      for (final msg in messages) {
        final msgId = msg['id'];
        final response = await http.get(
          Uri.parse(
              'https://gmail.googleapis.com/gmail/v1/users/me/messages/$msgId?format=full'),
          headers: headers,
        );
        final jsonData = jsonDecode(response.body);
        final payload = jsonData['payload']; //外枠のMessagePart構造体を取り出す
        final parts = payload['parts']; //partsフィールドを取り出す
        String? body;
        if (parts != null) { //メールは複数の要素に分かれている
          for (var part in parts) { //各要素のbodyを調べる
            final partData = part['body'];
            if (partData['size'] > 0) {
              final data = partData['data'];
              final decodedData = utf8.decode(base64Url.decode(data));
              body = decodedData;
            }
          }
        } else { //メールは単一の要素からなる
          final data = payload['body']['data']; //単に外枠のbodyを取り出せば良い
          final decodedData = utf8.decode(base64Url.decode(data));
          body = decodedData;
        }
        listRawTexts.add(body);
      }
      return listRawTexts;
    } else {
      throw Exception('Failed to fetch emails');
    }

参考文献

本文中で直接リンクを示していないもののみ挙げます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?