Porting REST calls to SharePoint Framework

The SharePoint Framework (SPFx) is a powerful technology for developing web parts that run on both classic and modern SharePoint pages. SPFx is 100% client-side, and it’s often possible to reuse client-side JavaScript (from Script Editor or Content Editor Web Parts) in SPFx. This is usually straight forward if the original JavaScript targeted a single HTML element, since SPFx does the same thing: it hands you an HTML element and you inject your web part in there.

I was recently porting a JavaScript widget to SPFx and it all came over more or less as expected, except that the REST calls to SharePoint failed. All of them. Failed. Miserably. This article will show you the fix.

The failure might seem odd because, well, JavaScript is JavaScript and SharePoint is SharePoint so why shouldn’t the same JavaScript running on a page in the same server work the same? The reason it failed is because the original developer used information that SharePoint injects on classic pages – specifically an object called _spPageContextInfo and a hidden form field called __REQUESTDIGEST. However this information is absent from modern pages. The result is an SPFx web part that works on a classic page but not a modern one.

In his excellent article on migrating JavaScript widgets to SPFx, Waldek Mastykarz addresses this by rewriting the REST calls to use the SPFx’s built-in HTTP client. Certainly this was Microsoft’s intent, and it does have the advantage of ensuring the request digest is refreshed (see below).  But I had a lot of code and didn’t want to rewrite it, and the request digest was only used when the web part first renders, so it seemed safe to not worry about refreshing. So I wrote a helper class that solves the problem and allows existing code to run without change. (This approach was suggested by Chakkaradeep Chandran, a Sr. PM on the SPFx team; thanks Chaks!)

The class is in this gist. It retrieves most (not all) of the values in _spPageContextInfo from the SharePoint Framework page context, and builds the same object right where your code expects it to be. It also grabs the latest request digest and injects it into a hidden form field, again right where your code expects it to be. That fixed the old widget code, turning miserable failure into joyous success!

To use it, add the widgetHelper.ts class to your project and import it into your web part:

// (within a SharePoint Framework web part)
import { WidgetHelper } from './services/widgetHelper';

Then wrap your render logic in a call to ensureClassicPageProps() as follows:

public render(): void {
  var helper = new WidgetHelper(this.context);
  helper.ensureClassicPageProps(true).then (() => {
    const element: React.ReactElement<IEventsProps> =
        React.createElement(Events, { } );
    ReactDom.render(element, this.domElement);
  });
}

ensureClassicPageProps(false) will skip the request digest and just populate _spPageContextInfo; ensureClassicPageProps(true) will get both.

Getting the Request Digest

I won’t bore you with a detailed walk-through of the helper class, but I do want to show you how to retrieve the request digest from SPFx in case you want to do this yourself. The typical approach is to make a 2nd REST call, as Wictor Wilén details in this article. This works well, but I didn’t want to make a 2nd REST call unless I really needed to. Wouldn’t it be nice if there was a way to get an up-to-date digest and share it with all the other web parts so each one doesn’t need to do an extra REST call? Well there is! The SPFx HttpClient object already does this work; this is mentioned in the Microsoft documentation, but the code there wasn’t up-to-date and didn’t work. So I dug into the SPFx code to figure it out:

import { IDigestCache } from '@microsoft/sp-http';
// ...
var digestCache: IDigestCache =
  this.wpContext.serviceScope.consume(DigestCache.serviceKey);
digestCache.fetchDigest(this.wpContext.pageContext.web.serverRelativeUrl)
  .then ((digest:string) => {
    // (use the digest somehow)
  })

This works nicely and has a few advantages:

  • It’s less work than doing it yourself
  • It’s shared by the whole page, avoiding making redundant REST calls
  • SPFx takes care of refreshing the digest

Refreshing the Request Digest

In a twitter conversation, Waldek pointed out that this approach – putting the request digest in a hidden form field – does not handle request digest refreshing. The digest expires after 30 minutes; Wictor’s article is all about how to refresh it. Wictor also points out that obtaining the digest from the hidden form field (where it is not refreshed) is “part of basically every SharePoint POST/REST sample on the interwebs.” So most of the legacy code you’re likely to port will work that way. My helper class doesn’t address that. If the legacy code ignored the request digest refresh issue (as most code did), it’s not going to magically change.

My opinion on this is that it’s OK if you’re going to make your requests immediately after you render your web part. On the other hand, if your web part might sit on the screen for half an hour and then the user pushes a button and it makes a request, the request digest may have timed out and the request will fail. You have to decide if this is an issue for your situation; if you want proper digest refreshing, and the code you’re porting ignores this, you’ll need to change the code. Options include using the SPFx Http Client or (and I highly recommend this), the SharePoint PnP JavaScript library.

I hope this is helpful; please comment with any ideas, fixes, etc. Thanks!

4 thoughts on “Porting REST calls to SharePoint Framework

  1. You don’t need to build up the _spPageContextInfo object. All the properties are available via this.context.pageContext.legacyPageContext

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s