1

Trying to work out the $batch REST api in sharepoint 365

My request payload (simple GET request)

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

Creating One item and getting it back

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

The response I get is for both of them.

{"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'."
    }
  }
}

The request is exactly similar to the docs and still no luck:

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

Can someone help?

Update code added

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

}


1 Answer 1

0

I am not sure what is wrong with your code. It may be possible that batch request headers and payload not generated properly.

Using Below line of code I am able to create item using $batch API On SharePoint Online. I have used BatchUtils, which is handling all stuff for generating headers and body for batch API.

// Prepare collection of request with requestUrl and payload data.
var arr=[{
reqUrl:"https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items"
,action:"ADD",
data:{__metadata:{type:"SP.Data.PlaceHolderListListItem"},Title:"Add Article_1"}}
,{
reqUrl:"https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(215)"
,action:"DELETE"}];


BatchUtils.PostBatchAll({rootUrl:"https://brgrp.sharepoint.com",
batchUrls:arr}).then(r=>console.log(r));

Reference links

  1. Batch API Example
  2. https://www.c-sharpcorner.com/article/sharepoint-batch-request-for-add-list-item/
1
  • Yes I tried the library same response. I posted my code
    – Joy Biswas
    Aug 10, 2019 at 5:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.