Пакетирование запросов

В этом документе показано, как объединять вызовы API в пакеты, чтобы сократить количество HTTP-подключений, которые должен устанавливать ваш клиент.

Этот документ посвящен созданию пакетного запроса посредством отправки HTTP-запроса. Если вы используете клиентскую библиотеку Google для создания пакетного запроса, см. документацию к клиентской библиотеке .

Обзор

Каждое HTTP-соединение, устанавливаемое вашим клиентом, приводит к определённым накладным расходам. API Google Classroom поддерживает пакетную обработку, что позволяет вашему клиенту объединять несколько вызовов API в один HTTP-запрос.

Примеры ситуаций, когда вам может понадобиться использовать пакетную обработку:

  • Получение списков для большого количества курсов.
  • Массовое создание или обновление курсов.
  • Добавление большого количества списков курсов.
  • Получение списков курсов для большого количества пользователей.

В каждом случае, вместо того, чтобы отправлять каждый вызов по отдельности, вы можете объединить их в один HTTP-запрос. Все внутренние запросы должны быть направлены к одному и тому же API Google.

Количество вызовов в одном пакетном запросе ограничено 50. Если вам необходимо сделать больше вызовов, используйте несколько пакетных запросов.

Примечание : пакетная система для API Google Classroom использует тот же синтаксис, что и система пакетной обработки OData , но семантика отличается.

Подробности партии

Пакетный запрос состоит из нескольких вызовов API, объединённых в один HTTP-запрос, который можно отправить по batchPath указанному в документе API Discovery . Путь по умолчанию — /batch/ api_name / api_version . В этом разделе подробно описан синтаксис пакетного запроса; далее будет приведён пример .

Примечание : набор из n запросов, объединённых в пакет, учитывается в вашем лимите использования как n запросов, а не как один запрос. Пакетный запрос разделяется на набор запросов перед обработкой.

Формат пакетного запроса

Пакетный запрос — это один стандартный HTTP-запрос, содержащий несколько вызовов API Google Classroom с использованием типа контента multipart/mixed . В этом основном HTTP-запросе каждая часть содержит вложенный HTTP-запрос.

Каждая часть начинается с собственного HTTP-заголовка Content-Type: application/http . Она также может иметь необязательный заголовок Content-ID . Однако заголовки частей служат лишь для обозначения начала части; они отделены от вложенного запроса. После того как сервер разворачивает пакетный запрос на отдельные запросы, заголовки частей игнорируются.

Тело каждой части представляет собой полный HTTP-запрос с собственной командой, URL, заголовками и текстом. HTTP-запрос должен содержать только часть пути URL; полные URL-адреса в пакетных запросах не допускаются.

HTTP-заголовки внешнего пакетного запроса, за исключением заголовков Content- , таких как Content-Type , применяются к каждому запросу в пакете. Если указать один и тот же HTTP-заголовок и во внешнем запросе, и в отдельном вызове, то значение заголовка отдельного вызова переопределит значение заголовка внешнего пакетного запроса. Заголовки отдельного вызова применяются только к этому вызову.

Например, если вы предоставляете заголовок Authorization для конкретного вызова, то этот заголовок применяется только к этому вызову. Если вы предоставляете заголовок Authorization для внешнего запроса, то этот заголовок применяется ко всем отдельным вызовам, если только они не переопределяют его собственными заголовками Authorization.

Когда сервер получает пакетный запрос, он применяет параметры запроса внешнего запроса и заголовки (по мере необходимости) к каждой части, а затем обрабатывает каждую часть так, как если бы это был отдельный HTTP-запрос.

Ответ на пакетный запрос

Ответ сервера представляет собой один стандартный HTTP-ответ с типом содержимого multipart/mixed ; каждая часть является ответом на один из запросов в пакетном запросе, в том же порядке, что и запросы.

Как и части запроса, каждая часть ответа содержит полный HTTP-ответ, включая код состояния, заголовки и тело. Как и части запроса, каждая часть ответа предваряется заголовком Content-Type , который отмечает начало части.

Если заданная часть запроса имела заголовок Content-ID , то соответствующая часть ответа имеет соответствующий заголовок Content-ID , при этом исходному значению предшествует строка response- , как показано в следующем примере.

Примечание : Сервер может выполнять ваши вызовы в любом порядке. Не рассчитывайте на то, что они будут выполнены в указанном вами порядке. Если вы хотите гарантировать, что два вызова будут выполнены в заданном порядке, вы не можете отправить их в одном запросе. Вместо этого отправьте первый вызов отдельно, а затем дождитесь ответа на первый, прежде чем отправлять второй.

Пример

В следующем примере показано использование пакетной обработки с API Google Classroom.

Пример пакетного запроса

POST https://classroom.googleapis.com/batch HTTP/1.1
Authorization: Bearer your_auth_token
Content-Type: multipart/mixed; boundary=batch_foobarbaz
Content-Length: total_content_length

--batch_foobarbaz
Content-Type: application/http
Content-Transfer-Encoding: binary
MIME-Version: 1.0
Content-ID: <item1:12930812@classroom.example.com>

PATCH /v1/courses/134529639?updateMask=name HTTP/1.1
Content-Type: application/json; charset=UTF-8
Authorization: Bearer your_auth_token

{
  "name": "Course 1"
}
--batch_foobarbaz
Content-Type: application/http
Content-Transfer-Encoding: binary
MIME-Version: 1.0
Content-ID: <item2:12930812@classroom.example.com>

PATCH /v1/courses/134529901?updateMask=section HTTP/1.1
Content-Type: application/json; charset=UTF-8
Authorization: Bearer your_auth_token
{
  "section": "Section 2"
}
--batch_foobarbaz--

Пример пакетного ответа

Это ответ на пример запроса в предыдущем разделе.

HTTP/1.1 200
Content-Length: response_total_content_length
Content-Type: multipart/mixed; boundary=batch_foobarbaz

--batch_foobarbaz
Content-Type: application/http
Content-ID: <response-item1:12930812@classroom.example.com>

HTTP/1.1 200 OK
Content-Type application/json
Content-Length: response_part_1_content_length

{
  "id": "134529639",
  "name": "Course 1",
  "section": "Section 1",
  "ownerId": "116269102540619633451",
  "creationTime": "2015-06-25T14:23:56.535Z",
  "updateTime": "2015-06-25T14:33:06.583Z",
  "enrollmentCode": "6paeflo",
  "courseState": "PROVISIONED",
  "alternateLink": "http://classroom.google.com/c/MTM0NTI5NjM5"
}
--batch_foobarbaz
Content-Type: application/http
Content-ID: <response-item2:12930812@classroom.example.com>

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: response_part_2_content_length

{
  "id": "134529901",
  "name": "Course 1",
  "section": "Section 2",
  "ownerId": "116269102540619633451",
  "creationTime": "2015-06-25T14:23:08.761Z",
  "updateTime": "2015-06-25T14:33:06.490Z",
  "enrollmentCode": "so75ha5",
  "courseState": "PROVISIONED",
  "alternateLink": "http://classroom.google.com/c/MTM0NTI5OTAx"
}
--batch_foobarbaz--

Использование клиентских библиотек

В следующих примерах кода показано, как выполнять пакетные запросы с использованием клиентских библиотек API Google. Подробнее об установке и настройке библиотек см. в соответствующих кратких руководствах.

.СЕТЬ

classroom/snippets/ClassroomSnippets/BatchAddStudents.cs
using Google;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Classroom.v1;
using Google.Apis.Classroom.v1.Data;
using Google.Apis.Requests;
using Google.Apis.Services;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ClassroomSnippets
{
    // Class to demonstrate the use of Classroom Batch Add Students API
    public class BatchAddStudents
    {
        /// <summary>
        /// Add multiple students in a specified course.
        /// </summary>
        /// <param name="courseId">Id of the course to add students.</param>
        /// <param name="studentEmails">Email address of the students.</param>
        public static void ClassroomBatchAddStudents(string courseId,
            List<string> studentEmails)
        {
            try
            {
                /* Load pre-authorized user credentials from the environment.
                 TODO(developer) - See https://developers.google.com/identity for 
                 guides on implementing OAuth2 for your application. */
                GoogleCredential credential = GoogleCredential.GetApplicationDefault()
                    .CreateScoped(ClassroomService.Scope.ClassroomRosters);

                // Create Classroom API service.
                var service = new ClassroomService(new BaseClientService.Initializer
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Classroom Snippets"
                });

                var batch = new BatchRequest(service, "https://classroom.googleapis.com/batch");
                BatchRequest.OnResponse<Student> callback = (student, error, i, message) =>
                {
                    if (error != null)
                    {
                        Console.WriteLine("Error adding student to the course: {0}", error.Message);
                    }
                    else
                    {
                        Console.WriteLine("User '{0}' was added as a student to the course.",
                            student.Profile.Name.FullName);
                    }
                };
                foreach (var studentEmail in studentEmails)
                {
                    var student = new Student() {UserId = studentEmail};
                    var request = service.Courses.Students.Create(student, courseId);
                    batch.Queue<Student>(request, callback);
                }

                Task.WaitAll(batch.ExecuteAsync());
            }
            catch (Exception e)
            {
                // TODO(developer) - handle error appropriately
                if (e is AggregateException)
                {
                    Console.WriteLine("Credential Not found");
                }
                else if (e is GoogleApiException)
                {
                    Console.WriteLine("Course does not exist.");
                }
                else
                {
                    throw;
                }
            }
        }
    }
}

Ява

classroom/snippets/src/main/java/BatchAddStudents.java
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.classroom.Classroom;
import com.google.api.services.classroom.ClassroomScopes;
import com.google.api.services.classroom.model.Student;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/* Class to demonstrate the use of Classroom Batch Add Students API */
public class BatchAddStudents {

  /* Scopes required by this API call. If modifying these scopes, delete your previously saved
  tokens/ folder. */
  static ArrayList<String> SCOPES =
      new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_ROSTERS));

  /**
   * Add multiple students in a specified course.
   *
   * @param courseId - Id of the course to add students.
   * @param studentEmails - Email address of the students.
   * @throws IOException - if credentials file not found.
   * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.
   */
  public static void batchAddStudents(String courseId, List<String> studentEmails)
      throws GeneralSecurityException, IOException {

    // Create the classroom API client.
    final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
    Classroom service =
        new Classroom.Builder(
                HTTP_TRANSPORT,
                GsonFactory.getDefaultInstance(),
                ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))
            .setApplicationName("Classroom samples")
            .build();

    BatchRequest batch = service.batch();
    JsonBatchCallback<Student> callback =
        new JsonBatchCallback<>() {
          public void onSuccess(Student student, HttpHeaders responseHeaders) {
            System.out.printf(
                "User '%s' was added as a student to the course.\n",
                student.getProfile().getName().getFullName());
          }

          public void onFailure(GoogleJsonError error, HttpHeaders responseHeaders) {
            System.out.printf("Error adding student to the course: %s\n", error.getMessage());
          }
        };
    for (String studentEmail : studentEmails) {
      Student student = new Student().setUserId(studentEmail);
      service.courses().students().create(courseId, student).queue(batch, callback);
    }
    batch.execute();
  }
}

PHP

класс/snippets/src/ClassroomBatchAddStudents.php
<?php
use Google\Client;
use Google\Service\Classroom;
use Google\Service\Classroom\Student;
use Google\Service\Exception;

function batchAddStudents($courseId, $studentEmails)
{
    /* Load pre-authorized user credentials from the environment.
    TODO(developer) - See https://developers.google.com/identity for
     guides on implementing OAuth2 for your application. */
    $client = new Client();
    $client->useApplicationDefaultCredentials();
    $client->addScope("https://www.googleapis.com/auth/classroom.profile.emails");
    $service = new Classroom($client);
    $service->getClient()->setUseBatch(true);
    //create batch
    $batch = $service->createBatch();
    foreach ($studentEmails as $studentEmail) {
        $student = new Student([
            'userId' => $studentEmail
        ]);
        $request = $service->courses_students->create($courseId, $student);
        $requestId = $studentEmail;
        $batch->add($request, $requestId);
    }
    //executing request
    $results = $batch->execute();
    foreach ($results as $responseId => $student) {
        $studentEmail = substr($responseId, strlen('response-') + 1);
        if ($student instanceof Exception) {
            $e = $student;
            printf("Error adding user '%s' to the course: %s\n", $studentEmail,
                $e->getMessage());
        } else {
            printf("User '%s' was added as a student to the course.\n",
                $student->profile->name->fullName, $courseId);
        }
    }
    $service->getClient()->setUseBatch(false);
    return $results;
}

Питон

course_id = '123456'
student_emails = ['alice@example.edu', 'bob@example.edu']
def callback(request_id, response, exception):
    if exception is not None:
        print 'Error adding user "{0}" to the course course: {1}'.format(
            request_id, exception)
    else:
        print 'User "{0}" added as a student to the course.'.format(
            response.get('profile').get('name').get('fullName'))
batch = service.new_batch_http_request(callback=callback)
for student_email in student_emails:
    student = {
        'userId': student_email
    }
    request = service.courses().students().create(courseId=course_id,
                                                  body=student)
    batch.add(request, request_id=student_email)
batch.execute(http=http)