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: '*/*'
})
);
})
);
}
}