Вопрос

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.
Это было полезно?

Решение

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
            })
          ]
        }
      ]
    }
  ]
};
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с sharepoint.stackexchange
scroll top