Creazione di batch di richieste

Questo documento mostra come raggruppare le chiamate API per ridurre il numero di connessioni HTTP che il tuo client deve effettuare.

Questo documento riguarda nello specifico l'invio di una richiesta batch inviando una richiesta HTTP. Se invece utilizzi una libreria client Google per inviare una richiesta batch, consulta la documentazione della libreria client.

Panoramica

Ogni connessione HTTP stabilita dal client comporta un determinato carico. L'API Google Classroom supporta il raggruppamento per consentire al client di inserire più chiamate API in un'unica richiesta HTTP.

Esempi di situazioni in cui è possibile utilizzare la gestione in batch:

  • Recupero degli elenchi per un numero elevato di corsi.
  • Creazione o aggiornamento collettivo dei corsi.
  • Aggiunta di un numero elevato di elenchi di studenti dei corsi.
  • Recupero degli elenchi di corsi per un numero elevato di utenti.

In ogni caso, anziché inviare ogni chiamata separatamente, puoi raggrupparle in un'unica richiesta HTTP. Tutte le richieste interne devono andare alla stessa API di Google.

Puoi effettuare fino a 50 chiamate in una singola richiesta batch. Se devi effettuare più chiamate, utilizza più richieste collettive.

Nota: il sistema batch per l'API Google Classroom utilizza la stessa sintassi del sistema di elaborazione batch OData, ma la semantica è diversa.

Dettagli del batch

Una richiesta batch è composta da più chiamate API combinate in un'unica richiesta HTTP, che può essere inviata all'batchPath specificato nel documento di discovery dell'API. Il percorso predefinito è /batch/api_name/api_version. Questa sezione descrive la sintassi batch in dettaglio; di seguito è riportato un esempio.

Nota: un insieme di n richieste raggruppate viene conteggiato ai fini del limite di utilizzo come n richieste, non come una singola richiesta. La richiesta batch viene separata in un insieme di richieste prima dell'elaborazione.

Formato di una richiesta batch

Una richiesta batch è una singola richiesta HTTP standard contenente più chiamate all'API Google Classroom, che utilizza il tipo di contenuto multipart/mixed. All'interno di questa richiesta HTTP principale, ciascuna parte contiene una richiesta HTTP nidificata.

Ogni parte inizia con la propria intestazione HTTP Content-Type: application/http. Può anche avere un'intestazione Content-ID facoltativa. Tuttavia, le intestazioni della parte servono solo a contrassegnare l'inizio della parte e sono separate dalla richiesta nidificata. Dopo che il server ha scompattato la richiesta batch in richieste separate, le intestazioni delle parti vengono ignorate.

Il corpo di ogni parte è una richiesta HTTP completa, con verbo, URL, intestazioni e corpo. La richiesta HTTP deve contenere solo la parte di percorso dell'URL; gli URL completi non sono consentiti nelle richieste collettive.

Le intestazioni HTTP per la richiesta batch esterna, ad eccezione delle intestazioni Content- come Content-Type, si applicano a ogni richiesta del batch. Se specifichi una determinata intestazione HTTP sia nella richiesta esterna sia in una singola chiamata, il valore dell'intestazione della singola chiamata sostituisce il valore dell'intestazione della richiesta batch esterna. Le intestazioni di una singola chiamata si applicano solo a quella chiamata.

Ad esempio, se fornisci un'intestazione Authorization per una chiamata specifica, questa intestazione si applica solo a quella chiamata. Se fornisci un'intestazione Authorization per la richiesta esterna, questa intestazione si applica a tutte le singole chiamate, a meno che non vengano sostituite con intestazioni Authorization proprie.

Quando il server riceve la richiesta raggruppata, applica i parametri di query e le intestazioni della richiesta esterna (se appropriato) a ogni parte e poi tratta ogni parte come se fosse una richiesta HTTP separata.

Risposta a una richiesta batch

La risposta del server è una singola risposta HTTP standard con un tipo di contenuto multipart/mixed; ogni parte è la risposta a una delle richieste nella richiesta in batch, nello stesso ordine delle richieste.

Come le parti della richiesta, ogni parte della risposta contiene una risposta HTTP completa, inclusi un codice di stato, le intestazioni e il corpo. Come le parti nella richiesta, ogni parte della risposta è preceduta da un'intestazione Content-Type che ne indica l'inizio.

Se una determinata parte della richiesta aveva un'intestazione Content-ID, la parte corrispondente della risposta ha un'intestazione Content-ID corrispondente, con il valore originale preceduto dalla stringa response-, come mostrato nell'esempio seguente.

Nota: il server potrebbe eseguire le chiamate in qualsiasi ordine. Non aspettarti che vengano eseguite nell'ordine in cui le hai specificate. Se vuoi assicurarti che due chiamate avvengano in un determinato ordine, non puoi inviarle in un'unica richiesta. Devi inviare la prima chiamata da sola e attendere la risposta alla prima prima di inviare la seconda.

Esempio

L'esempio seguente mostra l'utilizzo del raggruppamento con l'API Google Classroom.

Esempio di richiesta batch

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--

Esempio di risposta batch

Questa è la risposta alla richiesta di esempio nella sezione precedente.

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--

Utilizzo delle librerie client

I seguenti esempi di codice mostrano come effettuare richieste batch utilizzando le librerie client delle API di Google. Consulta le rispettive guide rapide per ulteriori informazioni su come installare le librerie e configurarle.

.NET

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;
                }
            }
        }
    }
}

Java

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

classroom/snippets/src/ClassroomBatchAddStudents.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;
}

Python

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)