SharePoint Designer 2013 Workflows and POST REST API


Following on from my last post of using GET REST API in workflows, this post will show how you can create an item in a list using the POST REST API.

Creating a list Item using REST API.

Using the same list as before, on my SharePoint site, I have a list. The list is nothing special, it’s just an example of some data.

Title: String
Person: User field
Colour: Choice field

You wouldn’t do this in real world scenario, but my demo will demonstrate when you run the workflow on any item, it will just create a new item in the list.

A better real world scenario might be when an item is added to this list, some of the information needs to be copied and created in another list, or when an item is changed a workflow fires to copy the information to another list similar to a history list.

Creating the workflow

In SharePoint Designer, I have gone to my list, and created a new workflow.

  • First, we need to add a Dictionary Item. This is so we can create a header for our HTTP Call. Type in “Dictionary
    and then in the options to choose from select Build Dictionary.

    Click on “this”.

    A dialog will appear, and it is here we need to add two headers.
Name Type Value
accept String application/json;odata=verbose
content-type String application/json;odata=verbose

Click on Variable:dictionary, and create a new variable called Headers.

  • We need to create another Dictionary now, this is to store the “type”. When you create an item with REST API, you need to provide the __metadata with the ListItemEntityType. Typically this item is SP.Data.[ListName]Item. However, to be absolutely sure, you can use this REST API call in the browser to find out.

     

    https://<tenant>/sites/<Site>/_api/web/lists/getbytitle('	<listTitle>')/ListItemEntityTypeFullName
    
Name Type Value
type String SP.Data.DemoListItem

  • One more dictionary needs to be created, this is the RequestContent for the POST Call.
Name Type Value
__metadata Dictionary Variable:EntityType
Title String New Item
Colour String Blue
PersonId String Current Item:Created By (return field as: User Id Number)

When you create this, ensure that __metadata has 2 underscores in front of it.
All fields are the internal names.

User fields all have an Id field with it, in my case the Person field has a PersonId field, it is this that I set with another User Id.

Choice fields just accept a valid string.

Taxonomy fields are a little different, see section below.

  • Now we can call a HTTP web service. Underneath the dictionary, start typing “http” and press enter to insert Call HTTP Web Service.
    Click on “this” to open up the dialog box, and then click on the … for the string builder dialog box.

    I like to use as much dynamic values as possible so my URL in here looks like the following:

    [%Workflow Context:Current Site URL%]/_api/web/lists/getbytitle('[%Workflow Context:List Name%]')/items
    

    Set the HTTP method to HTTP POST.

    After entering the URL and setting the Method, click the dropdown at the far right on the line in SPD and select properties. Set the RequestHeaders to the Dictionary Variable:Headers you made in the first step. Set the RequestContent to the dictionary Variable:NewItemMetadata. Set the ResponseContent to a new variable, and the ResponseStatusCode to a new variable. Click OK.

    The ResponseContent will be populated with the results, and the ResponseStatusCode will be populated with Created if successful, or a different value if something went wrong.

  • After the Web service call, I add an If Statement, to test that the variable responseCode equals to Created.
  • I log success to the history list.
  • In the else statement of if “responseCode equals Created” I grab the error message from the ResponseContent dictionary and output the value to an error message. Then I display the error message in the history list. To grab the error the dictionary path is error/message/value

  • Don’t forget to Transition to Stage “End of Workflow” at the end.

Publish your workflow.

When I run the workflow against any list item, after a moment a new entry is added to the list.

Updating Taxonomy/managed metadata column in POST REST API calls.

To be able to update a Taxonomy column in a list you would need to create the following 2 dictionary’s first, then assign 2nd dictionary to the column.

TaxonomyMetadata Dictionary

Name Type Value
type String SP.Taxonomy.TaxonomyFieldValue

TaxonomyValue Dictionary

Name Type Value
__metadata Dictionary TaxonomyMetadata Dictionary
Label String <Label value>
TermGuid String <TermGuid>
WssId String -1

<Label Value> – the actual value of the taxonomy term
<TermGuid> – The guid of the taxonomy term found in the termstore.
The WssId can always be -1 as SharePoint can work that out itself, but it requires a value.

PostRequestContent

Name Type Value
__metadata Dictionary Variable:EntityType
Title String New Item
<TaxonomyColumnInternalName> Dictionary Variable:TaxonomyValue Dictionary

It is the final dictionary above that you would set in the web service call as the RequestContent.

Advertisements

Getting Fiddler to work with SharePoint Online


Something that I’ve always struggled with is getting Fiddler to provide me with REST API results for SharePoint online. These steps should help you.

Install and configure Fiddler

  1. First download and install Fiddler if you haven’t already. You can download fiddler at the following URL : https://www.telerik.com/download/fiddler Unfortunately you have to use an email address to be able to download it now.
  2. After first install of Fiddler, you will get an AppContainer Configuration dialog appear. Just click Cancel for now.
  3. Once Fiddler has opened, from the menu click Tools > Telerik Fiddler Options
  4. On the HTTPS tab ensure that Decrypt HTTPS traffic is selected
  5. Click OK. You will get a dialog asking if you should Trust the Fiddler Root certificate. It is fine to say Yes here.
  6. Say Yes to install the certificate.
  7. They really want to make sure, you are sure. Click Yes again.
  8. Fiddler’s root certificate has now been added to the Machine Root List.
  9. Close and Restart Fiddler.

Composing a REST URL for SharePoint Online.

First we need to log in to SharePoint online.

Now this is the point when I can never get it to work. I open IE/Edge, and instantly whenever I try to hit a https web page I get the following screen.

The trick here is to open Chrome and use that instead.

  1. Using Chrome, log in to your SharePoint online.
  2. In Fiddler, you should see in the left pane, it’s been capturing all your requests. Find a 200 result for your SharePoint site.
  3. In the right hand pane, take down the following information and store it in NotePad.
    1. FedAuth Cookie
    2. rtFA Cookie
  4. Above the right hand pane in Fiddler, one of the tabs is called Composer click on this tab.
  5. In the GET section put your REST API request E.g https://<YourOnlineName>.SharePoint.com/sites/Workflow/_api/web
  6. In the section below enter the following:
    Accept: application/json;odata=verbose
    Content-Type: application/json;odata=verbose
    Cookie:
    
  7. After cookie, you will want to put the rtFa=<rtFaCode>;FedAuth=<FedAuthCode>
  8. Click the Execute button at the top right of the screen. Once you have pressed it a call will be made and show up in the left hand pane. It’s easy to spot because the icon is for JSON.
  9. Double click on this entry and the right hand bottom pane, you will have your JSON response.

Performing Post Requests

There are a few more steps to complete to perform a post request, I’m going to walk you through the steps below. I already have a list called FiddlerList in my SharePoint online site. Currently it holds one item. (My site called Workflow as I was testing something with workflow before working on this post, this has nothing to do with the demo, hopefully it won’t confuse you)

  1. In Fiddler, from your previous composed GET request, first change the GET request to list all items currently in this list. My GET request is now https:// <YourOnlineName>.sharepoint.com/sites/Workflow/_api/web/Lists/getByTitle(‘FiddlerList’)/Items
  2. The results are show below. Take note of your type in the __metadata node. Mine is SP.Data.FiddlerListListItem
    you will need this later for adding an item. The type is normally SP.Data.<ListName>ListItem
  3. Now we need to create a POST request to the URI of /_api/contextinfo and then capture the FormDigestValue

  4. Now we have all the information required to create an item. Go back to the composed tab. Change the URI back to the way we had before to get the list items. https://<YourOnlineName&gt;.sharepoint.com/sites/Workflow/_api/web/Lists/getByTitle(‘FiddlerList’)/Items
  5. Add the X-RequestDigest: put in your FormDigestValue. Your page should look similar to below.
  6. Then in the bottom pane, the Request Body, put the following (Remember to change the type to match your list type).
    {'__metadata': {'type':'SP.Data.FiddlerListListItem'},
    'Title':'Created by Fiddler REST'
    };
    
  7. Click Execute
  8. If all has worked well you should get a 201 Create Response back, and see the item created in your list.

Update API Request

To perform an update to the list item, first we need to know what the Item ID is. At the end of the 201 create JSON response I can see that the Item I created has the ID of 2.

  1. In my composer I have changed my URI to https://<YourOnlineName>.sharepoint.com/sites/workflow/_api/web/Lists/getByTitle(‘FiddlerList’)/Items(2)
  2. In the top pane, I have set the content-length back to 0 and added IF-MATCH: * and X-HTTP-Method: PATCH this indicates that the post request is actually an update.
  3. Lastly in the bottom pane, Request Body, I have changed the Title.

    Note: Screen shot shows MERGE which is backwards compatible, should use newer command PATCH.
  4. After executing, I get a 204 response, and I can see the title has changed in my list.

Delete API Request

Lastly I’m going to delete my item.

  1. Change X-HTTP-Method to say DELETE instead of PATCH
  2. Remove the Request Body text.
  3. Click Execute, you will get a 200 response back.
  4. Your item has now been removed from the List.

The above should be enough to get you going. You can now use fiddler to test out your REST API calls.

Cross Domain and SharePoint Hosted Apps using REST


When building SharePoint Apps, because they sit in a different Domain to your SharePoint website there are “blocking mechanisms” that prevents it from retrieving data from the SharePoint website. To be able to communicate, you will need to use the Cross Domain library. The cross domain library is a JavaScript file known as SP.RequesterExecutor.js, which is referenced by the SharePoint app, and hosted in the SharePoint website. You can find the SP.RequesterExecutor.js file under the “_layouts/15/” directory.

In SharePoint 2013, REST (Representational State Transfer) is a great way of communicating with SharePoint websites. You can test out REST directly in the browser. For example, in your browser type:

http://<site>/_api/web/?$select=Title


In the above REST call, I have requested the Web and got the Title only. If I didn’t add $select=Title I would have retrieved all information about dev.cannonfodder.local web. For more information on to how to use REST it can be found at the following Microsoft website http://msdn.microsoft.com/en-gb/library/fp142385.aspx

Cannot see feeds?

If when you did the above REST query you didn’t see the XML feed, but received the following message, just follow these steps.

  • Access Internet Options. Then on the Content tab, click Settings.

  • In the Feed and Web Slice Settings untick Turn on feed reading view. Click OK, OK and then refresh your browser with the REST query in it. You should now be seeing the data similar to my initial screenshot.

Cross Domain project

I’m going to walk you through building a SharePoint Hosted App, that will make 2 different Cross Domain calls. One will obtain the Title of the Host website, and the second will obtain all the lists in the host site.

  • Open Visual Studio and create a new App for SharePoint 2013 project.
  • Point it to your development site, and set the hosting to SharePoint Hosted.
  • First open the AppManifest.xml file, and on the Permission tab, give the scope of Web the permission of Read.
  • On your default.aspx page, within the PlaceHolderMain ContentPlaceHolder put the following.
    <div>
    <h2>Please select a way to bring back the title from the host web:</h2>
    </div>
    <div><input id="btnCrossDomainGetTitle" onclick="getTitleXd()" type="button" value="Get Title via REST Cross Domain" /></div>
    <div>
    <h2>Please select a way to bring back all lists in the host web:</h2>
    </div>
    <div><input id="btnCrossDomainGetLists" onclick="getListsXd()" type="button" value="Get Lists via REST Cross Domain" /></div>
    
  • Now switch to the App.js file.
  • First thing we need to do is set up the page to load the SP.RequestExecutor.js script. We also need to obtain the SPHostUrl.
    //global variables.
    var hostwebUrl
    var appwebUrl;
    var web;
    // This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model
    /*Get the page ready*/
    $(document).ready(function () {
        hostwebUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
        appwebUrl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
        var scriptbase = hostwebUrl + "/_layouts/15/";
        $.getScript(scriptbase + "SP.RequestExecutor.js");
    });
    function getQueryStringParameter(paramToRetrieve) {
        var params = document.URL.split("?")[1].split("&");
        var strParams = "";
        for (var i = 0; i &amp;lt; params.length; i = i + 1) {
            var singleParam = params[i].split("=");
            if (singleParam[0] == paramToRetrieve)
                return singleParam[1];
        }
    }
     
  • Now we need to add the button click handler, and the code to call and display the Title.
    /*Button Click Get Title Cross Domain*/
    function getTitleXd() {
         execCrossDomainTitleRequest();
    }
    //Cross Domain Call to obtain HostWeb Title.
    function execCrossDomainTitleRequest() {
        var executor;
        executor = new SP.RequestExecutor(appwebUrl);
        var url = appwebUrl + "/_api/SP.AppContextSite(@target)/web/Title?@target='" + hostwebUrl + "'";
        executor.executeAsync({
            url: url,
            method: "GET",
            headers: { "Accept": "application/json; odata=verbose" },
            success: successTitleHandlerXD,
            error: errorTitleHandlerXD
        }
        );
    }
    //Success Title
    function successTitleHandlerXD(data) {
        var jsonObject = JSON.parse(data.body);
        $('#lblResultTitle').html"<b>Via Cross Domain the title is:</b> " + jsonObject.d.Title);
    }
    //Error with Title.
    function errorTitleHandlerXD(data, errorCode, errorMessage) {
        $('#lblResultTitle').html("Could not complete cross-domain call: " + errorMessage);
    }
    
  • Now let’s add the button click handler and the code to display the lists from the host web.
/*Button Click Get Lists Cross Domain*/
function getListsXd() { execCrossDomainListRequest(); }
//Cross Domain Call to obtain Host Web Lists
function execCrossDomainListRequest() {
    var executor;
    executor = new SP.RequestExecutor(appwebUrl);
    var url = appwebUrl + "/_api/SP.AppContextSite(@target)/web/lists?@target='" + hostwebUrl + "'";
    executor.executeAsync({
        url: url,
        method: "GET",
        headers: {"Accept" : "application/json; odata=verbose"},
        success: successListHandlerXD,
        error: errorListHandlerXD
    });
}
//Success Lists
function successListHandlerXD(data){
    var jsonObject = JSON.parse(data.body);
    //Get LIsts
    var lists = jsonObject.d.results;
    $('#lblResultLists').html("<"b>Via Cross Domain the lists are:</b>");
    //Loop through each item adding to the label.
    var listsHtml = $.each(lists, function(index, list){
        $('#lblResultLists').append(list.Title + " (" + list.ItemCount + ")
");
    });
}
//Error Lists
function errorListHandlerXD(data, errorCode, errorMessage){
    $('#lblResultLists').html("Could not complete cross-domain call: " + errorMessage);
}
  • Now let’s run the app and see it working.

Cross domain call without using SP.RequestExecutor.js

Now during my learning journey of Cross Domain, I discovered that I can still call into my host site, using a REST URL based on the App web URL. I don’t believe this is a supported way, as I haven’t found anything on Microsoft sites that confirm this, however I have got this working On-Prem and Autohosted in 365.

  • Back on our app, open up the Default.aspx file again.
  • Add the two following buttons, put the btnStandardRestGetTitle after the btnCrossDomainGetTitle button, but before the </div>. And put the btnStandardRestGetLists after the btnCrossDomainGetLists, but before the </div>

<input id="btnStandardRestGetTitle" onclick="getTitle()" type="button" value="Get Title standard REST" />

<input id="btnStandardRestGetLists" onclick="getLists()" type="button" value="Get Lists standard REST" />

  • Back in the App.js file. First we need to add a function that gets the actual path of the application Url.
//Obtains the path upto the actual application. E.g. http://app123.app.code/SubSite/CrossDomainApp
//gets http://app123.app.com/SubSite
function getUrlPath() {
    var webRel = _spPageContextInfo.webAbsoluteUrl;
    var lastIndex = webRel.lastIndexOf('/');
    var urlpath = webRel.substring(0, lastIndex);
    return urlpath;
}
  • Now we need to add the button click handler, and the code to call and display the Title.
/*Button Click Get Title Rest*/
function getTitle() {
    execRESTTitleRequest();
}
//REST Call to obtain HostWeb Title
function execRESTTitleRequest() {
    var url = getUrlPath() + "/_api/web/?$select=Title";
    $.ajax({
        url: url,
        method: "GET",
        headers: { "Accept": "application/json; odata=verbose" },
        success: successTitleHandler,
        error: errorTitleHandler
    });
}
//Success Title
function successTitleHandler(data) {
    $('#lblResultTitle').html("<b>Via REST the title is:</b>" + data.d.Title);
}
//Error Title
function errorTitleHandler(data, errorCode, errorMessage) {
    $('#lblResultTitle').html("Could not complete REST call: " + errorMessage);
}
  • Now let’s add the button click handler and the code to display the lists from the host web.
/*Button Click Get List Rest*/
function getLists() { execRESTListRequest(); }
//REST Call to obtain HostWeb Lists
function execRESTListRequest() {
    var url = getUrlPath() + "/_api/web/Lists";
    $.ajax({
        url: url,
        method: "GET",
        headers: { "Accept": "application/json; odata=verbose" },
        success: successListHandler,
        error: errorListHandler
    });
}
//Success List
function successListHandler(data) {
    var lists = data.d.results;
    $('#lblResultLists').html("<b>Via REST the lists are:</b><br/>");
    var listsHtml = $.each(lists, function (index, list) {
        $('#lblResultLists').append(list.Title + " (" + list.ItemCount + ")<br/>");
    });
}
//Error Lists
function errorListHandler(data, errorCode, errorMessage) {
    $('#lblResultLists').html("Could not complete REST call: " + errorMessage);
}
  • Now let’s run the App

You have learnt two ways of doing a Cross Domain call using REST. One way is the correct way recommend by Microsoft. The other way is a way I found, but I’m unsure if this is a valid way. It works and sometimes that’s all that matters.