בקשות אצווה

במסמך הזה נסביר איך לארוב קריאות ל-API כדי לצמצם את מספר חיבורי ה-HTTP שהלקוח צריך לבצע.

המסמך הזה עוסק ספציפית בשליחת בקשה באצווה באמצעות שליחת בקשת HTTP. אם במקום זאת אתם משתמשים בספריית לקוח של Google כדי לשלוח בקשות באצווה, עיינו במסמכי התיעוד של ספריית הלקוח.

סקירה כללית

כל חיבור HTTP שהלקוח שלך יוצר גורם לתקורה מסוימת. ה-API של Google Classroom תומך בקיבוץ תגים כדי לאפשר ללקוח להעביר מספר קריאות ל-API לבקשת HTTP אחת.

דוגמאות למצבים שבהם כדאי להשתמש בקיבוץ:

  • אחזור רשימות תלמידים למספר גדול של קורסים.
  • יצירה או עדכון של קורסים בכמות גדולה.
  • הוספת מספר גדול של רשימות תלמידים.
  • אחזור רשימות קורסים עבור מספר גדול של משתמשים.

בכל מקרה, במקום לשלוח כל קריאה בנפרד, ניתן לקבץ את כל הקריאות לבקשת HTTP אחת. כל הבקשות הפנימיות חייבות להפנות לאותו API של Google.

מוגבל ל-50 שיחות בבקשה אחת באצווה. אם אתם צריכים לבצע יותר שיחות, אפשר להשתמש בכמות גדולה של בקשות.

הערה: מערכת האצווה של Google Classroom API משתמשת בתחביר זהה לזה של מערכת עיבוד האצווה של OData, אבל הסמנטיקה שלה שונה.

פרטי אצווה

בקשה באצווה מורכבת ממספר קריאות ל-API שמשולבות בבקשת HTTP אחת, וניתן לשלוח אותן אל batchPath שצוין במסמך גילוי ה-API. נתיב ברירת המחדל הוא /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 לבקשה החיצונית, הכותרת הזו תחול על כל הקריאות הנפרדות, אלא אם הן יבטלו אותה עם כותרות הרשאה משלהן.

כאשר השרת מקבל את הבקשה המקובצת, הוא מחיל את הפרמטרים והכותרות של השאילתה החיצונית (לפי הצורך) על כל חלק, ולאחר מכן מתייחס לכל חלק כאילו היה בקשת HTTP נפרדת.

תשובה לבקשה באצווה

תגובת השרת היא תגובת HTTP רגילה אחת עם סוג תוכן multipart/mixed. כל חלק הוא תגובה לאחת מהבקשות בבקשה המקובצת, באותו סדר שבו הן מופיעות בבקשות.

בדומה לחלקים בבקשה, כל חלק בתגובה מכיל תגובת HTTP מלאה, כולל קוד סטטוס, כותרות וגוף. וכמו בחלקים בבקשה, לפני כל חלק תגובה מופיעה כותרת Content-Type שמציינת את תחילת החלק.

אם לחלק נתון בבקשה יש כותרת Content-ID, לחלק המתאים בתגובה יש כותרת Content-ID תואמת, והערך המקורי לפני המחרוזת response-, כפי שמוצג בדוגמה הבאה.

הערה: השרת יכול לבצע את הקריאות בכל סדר שתבחרו. אל תסתמך על כך שהן יבוצעו בסדר שבו ציינת אותן. אם רוצים לוודא ששתי שיחות מתבצעות בסדר נתון, אי אפשר לשלוח אותן בבקשה אחת. במקום זאת, צריך לשלוח את הראשונה עצמה, ואז להמתין לתגובה להודעה הראשונה לפני שליחת הבקשה השנייה.

דוגמה

בדוגמה הבאה מוסבר איך מקבצים יחד עם Google Classroom API.

בקשה לדוגמה באצווה

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

שימוש בספריות לקוח

דוגמאות הקוד הבאות מדגימות איך ליצור בקשות באצווה באמצעות ספריות הלקוח של Google APIs. כדי לקבל מידע נוסף על התקנת הספריות וההגדרה שלהן, תוכלו להיעזר במדריכים למתחילים.

‎.NET

classroom/snippets/Classroomsnippets/BatchAdd לשאולs.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/BatchAdd לשאולs.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/ClassroomBatchAdd לשאולs.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)