Статьи

Освоение входящих сообщений с помощью API Gmail JavaScript

В этой статье мы собираемся создать базовое приложение Gmail для входящих сообщений и просмотра сообщений, используя Gmail RESTful API . Цель этой статьи — дать вам хорошую отправную точку для создания новых интересных приложений JavaScript с помощью этого API. Мы собираемся использовать jQuery и Bootstrap, чтобы уменьшить вес кода, чтобы мы могли сосредоточиться на том, чтобы что-то работало быстро, не беспокоясь о кросс-браузерных несоответствиях JavaScript и базовых стилях.

Как всегда, полный код этой статьи можно найти в нашем репозитории GitHub .

Логотип Gmail

Включение Gmail API в вашей учетной записи Google

Прежде всего, нам нужно разрешить доступ к Gmail API, чтобы получить наши учетные данные API. Для этого нам нужно посетить консоль разработчика Google в нашем любимом веб-браузере. Оттуда нам нужно создать проект (или выбрать существующий) и перейти в раздел API. Выберите «Gmail API» в разделе «API Google Apps» и нажмите кнопку «Включить API».

Теперь нам нужно создать два набора учетных данных: один для идентификатора клиента OAuth 2.0 для веб-приложения и другой для создания ключа API браузера. Это можно сделать в разделе учетных данных консоли разработчика Google, нажав кнопку «Добавить учетные данные».

Добавить скриншот учетных данных

Для API ключа браузера нам нужно только заполнить поле «имя». Однако для производства я бы рекомендовал добавить реферер HTTP (это предотвратит злоупотребление нашим ключом API от неавторизованных доменов). Для идентификатора клиента OAuth 2.0 мы должны ввести хотя бы один авторизованный источник JavaScript. Для локальной среды разработки это, вероятно, будет http://localhost или аналогичный. Нам не нужно вводить авторизованный URI перенаправления.

Заполнив необходимые поля, мы сможем увидеть наши учетные данные в разделе «Учетные данные». Оставьте эту информацию открытой на вкладке браузера на будущее.

Подключение к Gmail API

Несмотря на то, что Gmail API является стандартным REST API с использованием OAuth 2.0, мы рекомендуем использовать собственные библиотеки JavaScript Google для подключения и работы с любыми API, созданными Google. Это связано с тем, что Google уже упаковал логику аутентификации и необходимые зависимости в один включаемый файл — для нас меньше работы!

Итак, обо всем по порядку — давайте настроим наш HTML-файл, который мы собираемся использовать в качестве основы нашего приложения. Для целей этого приложения мы собираемся включить весь наш код в один файл HTML. В производственной среде я рекомендую разбивать HTML, CSS и JavaScript на отдельные файлы.

 <!doctype html> <html> <head> <title>Gmail API demo</title> <meta charset="UTF-8"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css"> <style> .hidden{ display: none; } </style> </head> <body> <div class="container"> <h1>Gmail API demo</h1> <button id="authorize-button" class="btn btn-primary hidden">Authorize</button> <table class="table table-striped table-inbox hidden"> <thead> <tr> <th>From</th> <th>Subject</th> <th>Date/Time</th> </tr> </thead> <tbody></tbody> </table> </div> <script src="//code.jquery.com/jquery-1.11.3.min.js"></script> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> <script type="text/javascript"> var clientId = 'xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com'; var apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; var scopes = 'https://www.googleapis.com/auth/gmail.readonly'; </script> <script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script> </body> </html> 

Справа внизу (над закрывающим </body> ) мы включаем клиентскую библиотеку Google JavaScript. Обратите внимание на строку запроса в конце, она содержит функцию обратного вызова, которая будет выполнена после загрузки скрипта — мы будем использовать ее для инициализации нашего приложения чуть позже. Чуть выше, где мы определяем наши учетные данные API, нам нужно вставить их из раздела Учетные данные консоли разработчика Google. Мы также определяем, какие разрешения нам потребуются от пользователя, они называются областями действия . Для целей этого приложения нам требуется только доступ только для чтения Gmail. Рекомендуется запрашивать у пользователя как можно меньше разрешений — это дает пользователю уверенность в том, что мы не собираемся делать что-то гнусное, например отправлять электронные письма от их имени без их ведома.

Кроме этого, у нас есть кнопка, которая позволит пользователю авторизовать нас для доступа к своей учетной записи Gmail, а также мы удалили таблицу для хранения наших входящих данных, как только мы их получим. И, как упоминалось ранее, мы включили необходимые файлы для jQuery и Bootstrap.

Аутентификация пользователя

Теперь мы собираемся предоставить пользователю механизм аутентификации для доступа к его учетной записи Gmail. Как упоминалось выше, нам необходимо создать функцию с именем handleClientLoad() которая будет автоматически вызываться после загрузки клиентской библиотеки Google JavaScript на страницу. Эта функция затем вызовет цепочку других функций, которые в конечном итоге приведут нас к извлечению их входящих сообщений.

 function handleClientLoad() { gapi.client.setApiKey(apiKey); window.setTimeout(checkAuth, 1); } function checkAuth() { gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: true }, handleAuthResult); } function handleAuthClick() { gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false }, handleAuthResult); return false; } function handleAuthResult(authResult) { if(authResult && !authResult.error) { loadGmailApi(); $('#authorize-button').remove(); $('.table-inbox').removeClass("hidden"); } else { $('#authorize-button').removeClass("hidden"); $('#authorize-button').on('click', function(){ handleAuthClick(); }); } } function loadGmailApi() { gapi.client.load('gmail', 'v1', displayInbox); } 

Мы должны вставить этот код непосредственно ниже, где мы устанавливаем учетные данные API, в том же блоке SCRIPT.

Чтобы подвести итог процесса, эта цепочка вызовов функций проходит:

  1. handleClientLoad() просто устанавливает ключ API и передает checkAuth() через 1 миллисекунду.
  2. checkAuth() проверяет, checkAuth() ли пользователь предварительную проверку подлинности нашего приложения в Google. Установка для immediate параметра значения true здесь означает, что мы не будем запрашивать у пользователя модальный логин / права доступа, если они не аутентифицированы. Затем мы передаем результат аутентификации в handleAuthResult() .
  3. handleAuthResult() выполняет одно из двух действий; если пользователь уже аутентифицирован, он загрузит Gmail API с помощью loadGmailApi() , в качестве альтернативы он отобразит кнопку авторизации в пользовательском интерфейсе и прикрепит к ней событие click, которое вызовет handleAuthClick()
  4. handleAuthClick() просто выполняет ту же функцию аутентификации, что и checkAuth() но предоставит пользователю модальный вход в систему / права доступа. Как только пользователь аутентифицирует ту же handleAuthResult() из handleAuthResult() запускается.
  5. После того, как эти серии функций выполнены и пользователь прошел аутентификацию, мы всегда должны оказаться в функции loadGmailApi() . Это просто загружает функциональность API Gmail из клиентской библиотеки Google JavaScript, а затем вызывает нашу displayInbox() .

Совет . Вы можете проверить (и отозвать), какие приложения имеют доступ к вашей учетной записи Gmail на этой странице: https://security.google.com/settings/security/permissions . Это может пригодиться при тестировании.

Извлечение и отображение входящих сообщений пользователя

Теперь, когда мы получили аутентификацию пользователя, мы можем перейти к отображению некоторых из его данных с помощью нашей функции displayInbox() . Нам нужно построить эту функцию со следующими компонентами;

Прежде всего нам нужно получить список сообщений из Gmail. Для этого нам нужно вызвать конечную точку списка Users.messages:. Для целей этого приложения мы будем запрашивать последние десять сообщений, помеченных как INBOX :

 function displayInbox() { var request = gapi.client.gmail.users.messages.list({ 'userId': 'me', 'labelIds': 'INBOX', 'maxResults': 10 }); request.execute(function(response) { $.each(response.messages, function() { var messageRequest = gapi.client.gmail.users.messages.get({ 'userId': 'me', 'id': this.id }); messageRequest.execute(appendMessageRow); }); }); } 

Это вернет объект JSON, содержащий идентификаторы последних десяти сообщений, полученных аутентифицированным пользователем, а также некоторые другие периферийные данные, которые нам не нужны. Обратите внимание, что мы можем использовать специальный userId от me чтобы указать текущего аутентифицированного пользователя. Как и для всех запросов API, выполняемых с помощью библиотеки Google, запросы должны быть назначены переменной, а затем должна быть вызвана функция execute() для фактического выполнения запроса. Эта функция принимает функцию обратного вызова в качестве параметра и позволяет вам указать параметр для сохранения ответа.

На самом деле мы делаем два API-запроса, так как, к сожалению, конечная точка API списка возвращает только идентификаторы сообщений, но не фактические данные сообщений. Таким образом, внутри нашей функции обратного вызова нам нужно перебирать каждое сообщение и запрашивать дополнительные данные, относящиеся к этому сообщению. Для этого нам нужно вызвать User.messages: получить конечную точку, чтобы получить одно сообщение по его идентификатору и передать ответ другой функции appendMessageRow() .

Теперь у нас есть данные сообщения, и мы наконец готовы изменить DOM и показать пользователю что-то!

 function appendMessageRow(message) { $('.table-inbox tbody').append( '<tr>\ <td>'+getHeader(message.payload.headers, 'From')+'</td>\ <td>'+getHeader(message.payload.headers, 'Subject')+'</td>\ <td>'+getHeader(message.payload.headers, 'Date')+'</td>\ </tr>' ); } 

Здесь мы используем функцию append () jQuery для добавления строк, содержащих данные сообщения, в таблицу HTML, которую мы ранее оформили. Это должно оставить нас с рабочим приложением, которое может показать пользователю последние десять сообщений из их почтового ящика! Но это не очень полезно, если вы не можете прочитать письмо, верно?

Примечание : если вы следуете этому руководству, вам также понадобится служебная функция getHeader() чтобы код работал на этом этапе. Вы можете прочитать о функциях утилит в конце статьи .

Отображение содержимого сообщения электронной почты

Поскольку мы уже получили содержимое сообщения из нашего Users.messages: get request, больше нет необходимости делать больше запросов API для отображения этих данных. Нам просто нужно встроить механизм в наш существующий код, чтобы облегчить отображение существующих данных, которые мы ранее извлекли.

Для этого нам нужно начать с добавления метода для запуска средства просмотра содержимого сообщения. Итак, мы собираемся изменить код appendMessageRow() сверху, чтобы добавить ссылку на ячейку таблицы объекта.

 function appendMessageRow(message) { $('.table-inbox tbody').append( '<tr>\ <td>'+getHeader(message.payload.headers, 'From')+'</td>\ <td>\ <a href="#message-modal-' + message.id + '" data-toggle="modal" id="message-link-' + message.id+'">' + getHeader(message.payload.headers, 'Subject') + '</a>\ </td>\ <td>'+getHeader(message.payload.headers, 'Date')+'</td>\ </tr>' ); } 

При этом используется модальная функциональность Bootstrap для запуска предварительно определенного модального окна при нажатии на ссылку. Итак, теперь нам нужно внести еще одну модификацию в код, чтобы также построить контейнер модального окна в DOM каждый раз, когда мы вставляем сводку сообщения в таблицу. Поэтому мы просто добавляем этот фрагмент под наш предыдущий фрагмент append() .

 $('body').append( '<div class="modal fade" id="message-modal-' + message.id + '" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">\ <div class="modal-dialog modal-lg">\ <div class="modal-content">\ <div class="modal-header">\ <button type="button"\ class="close"\ data-dismiss="modal"\ aria-label="Close">\ <span aria-hidden="true">&times;</span></button>\ <h4 class="modal-title" id="myModalLabel">' + getHeader(message.payload.headers, 'Subject') + '</h4>\ </div>\ <div class="modal-body">\ <iframe id="message-iframe-'+message.id+'" srcdoc="<p>Loading...</p>">\ </iframe>\ </div>\ </div>\ </div>\ </div>' ); 

Обратите внимание, что мы здесь просто окаменяем панель содержимого сообщения, и мы делаем это в iframe. Iframe используется потому, что если мы просто вставим содержимое сообщения прямо в DOM, это может привести к поломке нашего собственного приложения множеством способов. Любой, кто окунулся в мутную воду создания электронной почты HTML, может сказать вам, что сломанный старый HTML и встроенный перегруженный CSS — обычное дело, поэтому, если мы вставим этот код непосредственно в DOM, он может нанести ущерб эстетике наше приложение.

Мы также не хотим вставлять наш HTML-код непосредственно в iframe, когда мы создаем модальное пространство по нескольким причинам. Одним из них является несовместимость браузера, а другим — то, что если мы извлечем и отобразим десять внешних HTML-страниц (в комплекте с изображениями) в фоновом режиме при загрузке страницы, это может повлиять на скорость инициализации нашего приложения.

Итак, теперь у нас есть таблица деталей сообщения и модальное окно с пустой панелью содержимого, так что пришло время реализовать механизм отображения содержимого сообщения. Внутри этой же функции нам нужно прикрепить событие on click к нашим ссылкам на ячейки темы, чтобы отобразить содержимое iframe после запроса модального сообщения.

 $('#message-link-'+message.id).on('click', function(){ var ifrm = $('#message-iframe-'+message.id)[0].contentWindow.document; $('body', ifrm).html(getBody(message.payload)); }); 

Это просто обращается к iframe (который уже присутствует в DOM) и вставляет HTML-код нашего сообщения в его элемент <body> . Для достижения этой функциональности требуется небольшой обходной путь .

Сервисные функции

Теперь вы, возможно, спрашивали, что это за функции, которые мы использовали в предыдущих нескольких фрагментах кода; а именно getBody() , getHeader() и getHTMLPart() . Это служебные функции, которые мы определили для отвлечения некоторых нюансов от работы с ресурсом сообщений API Gmail, который возвращает электронные письма, состоящие из нескольких частей, в несогласованном формате (вложенные части), а также с телом сообщения base64 и UTF-8. закодирован. (Полный исходный код для этих функций доступен на нашем репозитории GitHub ).

Маленький стиль

Чтобы округлить наше приложение, добавьте этот CSS в раздел <head> нашей HTML-страницы:

 iframe { width: 100%; border: 0; min-height: 80%; height: 600px; display: flex; } 

Заключительные замечания

Теперь у нас должно быть работающее приложение, которое может отображать сводный список последних сообщений, а также отображать полную электронную почту в формате HTML.

Gmail демонстрационное приложение в действии

Очевидно, что с этим приложением есть много возможностей для улучшения, а именно:

  • Лучшее форматирование даты с использованием JavaScript-объекта Date
  • Усы или Руль HTML-шаблонизаторы (чтобы HTML не входил в JavaScript)
  • Правильный порядок сортировки сообщений по дате (в настоящее время это отображается не по порядку, потому что отдельные запросы к данным сообщения являются асинхронными, поэтому в зависимости от того, кто из них вернется первым, сначала идет таблица)
  • Возможность получить более 10 сообщений и разбить результаты на страницы
  • Ajax автообновление сообщений (с уведомлениями браузера о новых сообщениях)

Я также хотел бы взглянуть на добавление дополнительной функциональности в это приложение, очевидные следующие шаги могут быть:

  • Добавление дополнительных функций электронной почты, таких как создание, ответ, пересылка и т. Д. (Потребуется дополнительный запрос разрешений)
  • Отличить пользовательский интерфейс от того, что Gmail уже предлагает

Если у вас есть какие-либо другие улучшения или предложения, пожалуйста, не стесняйтесь добавлять их в комментариях.

Полный исходный код доступен через репозиторий GitHub .