4

Problem

I'm trying to create/update records in a list using SPFx. It's working as expected in IE, but I'm getting a 403 - FORBIDDEN in Chrome.

Request Digest

I am fetching my request digest value as such described by the documentation:

var digestCache:IDigestCache = this.context.serviceScope.consume(digestCacheServiceKey);
digestCache.fetchDigest(this.context.pageContext.web.serverRelativeUrl).then((digest: string) => {
  // Do Something with the digest
  console.log(digest);
});

I've seen other posts where it doesn't seem necessary to have to fetch the digest value, like Vardhaman's POST request post. For me, when I removed the fetchDigest call, I started getting a 403 in IE.

Code

Here's my createListItem method:

public createListItem(item: IMyListItem): Promise<IMyListItem | void> {
  const { Title } = item;
  const digestCache: IDigestCache = this._context.serviceScope.consume(DigestCache.serviceKey);

  return digestCache.fetchDigest(this._context.pageContext.web.serverRelativeUrl)
    .then((digest: string) => {
      const spOpts: ISPHttpClientOptions = {
        headers: {
          "Accept": "application/json;odata=verbose",
          "Content-Type": "application/json;odata=verbose",
          "X-RequestDigest": digest
        },
        body: `{
          __metadata: { "type": "SP.Data.MyListListItem" },
          Title: "${Title}"
        }`
      };

      return this._context.httpClient.post(
        `${this._context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('My List')/items`,
        SPHttpClient.configurations.v1,
        spOpts
      )
      .then((response: SPHttpClientResponse) => {
        console.log("After creation response", response);

        response.json().then((responseJSON: JSON) => {
          console.log("JSON", responseJSON);
        });

        if (response.ok) {
          return item;
        }
        return;

      })
      .catch((error: SPHttpClientResponse) => {
        console.log(error);
        return;
      });
    });
}

My updateListItem method is the same with the difference being the header and the added item id in the REST call.

updateListItem headers:

      headers: {
        "Accept": "application/json;odata=verbose",
        "Content-Type": "application/json;odata=verbose",
        "odata-version": "",
        "IF-MATCH": "*",
        "X-HTTP-Method": "MERGE",
        "X-RequestDigest": digest
      }

Notes

  • User permissions are set correctly.
  • Have tried this in the /_layouts/15/workbench.aspx as well as publishing app to app catalog and trying as an app.
  • Have tried multiple apps with separate types of POST requests.
  • 1
    Using the __RequestDigest is not the recommended approach in SPFx as noted in the referenced documentation. Are you porting old code? Otherwise, it is far easier to use the spHttpClient or even the PnP JS Core Library to take advantage of the SP REST API – theChrisKent Aug 4 '17 at 19:58
  • @theChrisKent After reading your comment and taking a closer look, I saw that I was using httpClient rather than spHttpClient. It seems to be working now without using __RequestDigest. If you want to convert your comment into an answer I can mark that as the solution. Thanks! – Jordan Aug 4 '17 at 20:36
4

With the General Availability release, we have to use SpHttpClient. i had to convert the old code that used httpclient to spHttpClient. You can also try out PnP JS in SPFx to implement CRUD operation which provides easy to implement methods. I have written an article on it. You can find the detailed code and article here

enter image description here

export default class PnPspCrudWebPart extends BaseClientSideWebPart<IPnPspCrudWebPartProps> {


private AddEventListeners() : void{
document.getElementById('AddItem').addEventListener('click',()=>this.AddItem());
document.getElementById('UpdateItem').addEventListener('click',()=>this.UpdateItem());
document.getElementById('DeleteItem').addEventListener('click',()=>this.DeleteItem());
}

private _getListData(): Promise<ISPList[]> {
return pnp.sp.web.lists.getByTitle("EmployeeList").items.get().then((response) => {

return response;
});

}

private getListData(): void {

  this._getListData()
    .then((response) => {
     this._renderList(response);
   });
  }

  private _renderList(items: ISPList[]): void {
  let html: string = '<table class="TFtable" border=1 width=100% style="border-collapse: collapse;">';
 html += `<th>EmployeeId</th><th>EmployeeName</th><th>Experience</th><th>Location</th>`;
  items.forEach((item: ISPList) => {
  html += `
     <tr>
    <td>${item.ID}</td>
    <td>${item.EmployeeName}</td>
    <td>${item.Experience}</td>
    <td>${item.Location}</td>
    </tr>
    `; 
 });
 html += `</table>`;
const listContainer: Element = this.domElement.querySelector('#spGetListItems');
listContainer.innerHTML = html;
}



public render(): void {
  this.domElement.innerHTML = `

  <div class="parentContainer" style="background-color: lightgrey">
  <div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
  <div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
  <span class="ms-font-xl ms-fontColor-white" style="font-size:28px">Welcome to SharePoint Framework Development using PnP JS Library</span>
  <p class="ms-font-l ms-fontColor-white" style="text-align: left">Demo : SharePoint List CRUD using PnP JS and SPFx</p>
  </div>
  </div>
   <div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
    <div style="background-color:Black;color:white;text-align: center;font-
    weight: bold;font-size:18px;">Employee Details</div>

  </div>
<div style="background-color: lightgrey" >
<form >
   <br>
   <div data-role="header">
     <h3>Add SharePoint List Items</h3>
  </div>
  <div data-role="main" class="ui-content">
     <div >
        <input id="EmployeeName"  placeholder="EmployeeName"    />
        <input id="Experience"  placeholder="Experience"  />
        <input id="Location"  placeholder="Location"    />
     </div>
     <div></br></div>
     <div >
        <button id="AddItem"  type="submit" >Add</button>
     </div>
  </div>
  <div data-role="header">
     <h3>Update/Delete SharePoint List Items</h3>
  </div>
  <div data-role="main" class="ui-content">
     <div >
        <input id="EmployeeId"   placeholder="EmployeeId"  />
     </div>
     <div></br></div>
     <div >
        <button id="UpdateItem" type="submit" >Update</button>
        <button id="DeleteItem"  type="submit" >Delete</button>
       </div>
     </div>
   </form>
</div>
<br>
<div style="background-color: lightgrey" id="spGetListItems" />
</div>

`;
this.getListData();
this.AddEventListeners();
}

AddItem()
{  

 pnp.sp.web.lists.getByTitle('EmployeeList').items.add({    
 EmployeeName : document.getElementById('EmployeeName')["value"],
 Experience : document.getElementById('Experience')["value"],
 Location:document.getElementById('Location')["value"]
});
 alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Added !");

}

UpdateItem()
{  

var id = document.getElementById('EmployeeId')["value"];
pnp.sp.web.lists.getByTitle("EmployeeList").items.getById(id).update({
 EmployeeName : document.getElementById('EmployeeName')["value"],
 Experience : document.getElementById('Experience')["value"],
 Location:document.getElementById('Location')["value"]
 });
 alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Updated !");
  }

  DeleteItem()
  {    
    pnp.sp.web.lists.getByTitle("EmployeeList").items.
    getById(document.getElementById('EmployeeId')["value"]).delete();
   alert("Record with Employee ID : "+ document.getElementById('EmployeeId')["value"] + " Deleted !");
  }

    protected get dataVersion(): Version {
      return Version.parse('1.0');
   }

   protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
    pages: [
    {
      header: {
        description: strings.PropertyPaneDescription
      },
      groups: [
        {
          groupName: strings.BasicGroupName,
          groupFields: [
            PropertyPaneTextField('description', {
              label: strings.DescriptionFieldLabel
            })
          ]
        }
      ]
    }
  ]
};
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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