0

sharepoint 365 で $batch REST API を解決しようとしています

私のリクエスト ペイロード (単純な GET リクエスト)

--batch_45993c25-30ce-459d-830f-a5aabaf4901f
Content-Type: application/http
Content-Transfer-Encoding: binary

GET https://${tenant}.sharepoint.com/test/_api/Web/Lists/GetByTitle('List')/Items?$orderby=Title HTTP/1.1
Accept: application/json;odata=verbose

--batch_45993c25-30ce-459d-830f-a5aabaf4901f--

1 つのアイテムを作成して戻す

--batch_28fcce05-10f9-4362-e7fb-55208b1ec9d8
Content-Type: multipart/mixed; boundary=changeset_eabea18e-488d-46fc-ba25-841d268c61c4
Content-Length: 406
Content-Transfer-Encoding: binary

--changeset_eabea18e-488d-46fc-ba25-841d268c61c4
Content-Type: application/http
Content-Transfer-Encoding: binary

POST https://${tenant}.sharepoint.com/test/_api/Web/Lists/GetByTitle('List')/Items HTTP/1.1
Content-Type: application/json;odata=verbose

{"Title":"2","Message":"2","__metadata":{"type":"SP.Data.ListListItem"}}

--changeset_eabea18e-488d-46fc-ba25-841d268c61c4--

--batch_28fcce05-10f9-4362-e7fb-55208b1ec9d8
Content-Type: application/http
Content-Transfer-Encoding: binary

GET https://${tenant}.sharepoint.com/test/_api/Web/Lists/GetByTitle('List')/Items?$orderby=Title HTTP/1.1
Accept: application/json;odata=verbose

--batch_28fcce05-10f9-4362-e7fb-55208b1ec9d8--

私が得る応答は、それらの両方に対するものです。

{"error":
  {"code":"-1, Microsoft.Data.OData.ODataContentTypeException",
  "message":
    {"lang":"en-US",
    "value":"A supported MIME type could not be found that matches the content type of the response. None of the supported type(s) 'multipart/mixed' matches the content type 'application/json;odata=verbose'."
    }
  }
}

リクエストはドキュメントとまったく同じですが、まだうまくいきません:

https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/make-batch-requests-with-the-rest-apis

誰か助けてくれませんか?

更新: コードを追加

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map, switchMap, } from 'rxjs/operators';

const CRLF = '\r\n';

const SP_SITE = 'https://tenant.sharepoint.com/subsite';
const ROOT_SITE = 'https://tenant.sharepoint.com';

const CONTEXT_INFO = `${SP_SITE}/_api/contextinfo`;
const LIST_ITEMS_API = list => `${SP_SITE}/_api/Web/Lists/GetByTitle('${list}')/Items`;

/**
 *
 * SharepointService is the base class to Sharepoint
 */

@Injectable({
  providedIn: 'root'
})
export class SharepointService {


  /**
   * Constructor to init SharepointService
   */
  constructor(private httpClient: HttpClient) {
    this.generateBatch('create', 'MyList', [{ Title: '2', Message: '2' }]).subscribe(data => console.log(data));
  }

  /**
   * API Default headers merged with passed values
   * param [options] If headers are passed they are merged with defaults
   */
  private getHeaders(options?: object): { headers: HttpHeaders } {
    const defaults = {
      Accept: 'application/json;odata=verbose'
    };
    return { headers: new HttpHeaders(Object.assign(defaults, options || {})) };
  }

  /**
   * Context Info for Invasive operations REST 2013
   */
  private getContextInfo(): Observable<string> {
    return this.httpClient.post<any>(CONTEXT_INFO, {}, this.getHeaders())
      .pipe(
        map(res => res.d.GetContextWebInformation.FormDigestValue)
      );
  }

  /**
   * generates an unique identifier for batch operations
   */
  private generateUUID(): string {
    let dateTime = new Date().getTime();
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (item) => {
      // tslint:disable-next-line: no-bitwise
      const replacer = (dateTime + Math.random() * 16) % 16 | 0;
      dateTime = Math.floor(dateTime / 16);
      // tslint:disable-next-line: no-bitwise
      return (item === 'x' ? replacer : (replacer & 0x7 | 0x8)).toString(16);
    });
  }

  /**
   * Batch CRUD method
   * NOTE: not working Sharepoint syntax is confusing
   *
   * @param operation any of CRUD
   * @param listName the sharepoint list from where the record needs to be operated
   * @param data the data for the operation
   */
  private generateBatch<T>(operation, listName, data) {
    const batchGuid = this.generateUUID();
    const changeSetId = this.generateUUID();
    const batchContents = [];
    const changeContents = [];
    let dataSet = '';

    data.forEach(item => {
      changeContents.push(`--changeset_${changeSetId}`);
      changeContents.push(`Content-Type: application/http`);
      changeContents.push(`Content-Transfer-Encoding: binary`);
      changeContents.push(``);
      changeContents.push(`POST ${LIST_ITEMS_API(listName)} HTTP/1.1`);
      changeContents.push(`Content-Type: application/json;odata=verbose`);
      changeContents.push(``);
      changeContents.push(JSON.stringify({ ...item, __metadata: { type: `SP.Data.${listName}ListItem` } }));
      changeContents.push(``);
    });
    changeContents.push(`--changeset_${changeSetId}--`);

    dataSet = changeContents.join(CRLF);

    batchContents.push(`--batch_${batchGuid}`);
    batchContents.push(`Content-Type: multipart/mixed; boundary=changeset_${changeSetId}`);
    batchContents.push(`Content-Length: ${dataSet.length}`);
    batchContents.push(`Content-Transfer-Encoding: binary`);
    batchContents.push(``);
    batchContents.push(dataSet);
    batchContents.push(``);

    batchContents.push(`--batch_${batchGuid}`);
    batchContents.push(`Content-Type: application/http`);
    batchContents.push(`Content-Transfer-Encoding: binary`);
    batchContents.push(``);
    batchContents.push(`GET ${LIST_ITEMS_API(listName)} HTTP/1.1`);
    batchContents.push(`Accept: application/json;odata=verbose`);
    batchContents.push(``);

    batchContents.push(`--batch_${batchGuid}--`);

    const batchBody = batchContents.join(CRLF);

    return this.getContextInfo()
      .pipe(
        switchMap(requestDigest => {
          return this.httpClient.post<T>(`${ROOT_SITE}/_api/$batch`, batchBody, this.getHeaders({
            'Content-Type': `multipart/mixed; boundary=batch_${batchGuid}`,
            'X-RequestDigest': requestDigest,
            Accept: '*/*'
          })
          );
        })
      );
  }

}

4

1 に答える 1