Exception – The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map

Sathish Nadarajan
 
Solution Architect
July 24, 2017
 
Rate this article
 
Views
2736

I was writing a Remote Event Receiver and when trying to access, the exception “The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map” Occurred. Thought of sharing the solution to the community.

1. Go to Server Manager.

clip_image002

2. Click on Manage -> Add Roles and Features.

3. On the Features, make sure that all the features under WCF is selected and install.

clip_image004

4. Do an IISReset.

5. Refresh the Service.

clip_image006

Now, the service will be working as expected. Thanks.

Happy Coding,

Sathish Nadarajan.

Category : Exception, IIS

Author Info

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 15+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business ...read more
 

Create a Custom SharePoint Webpart will have Kendo grid to fetch/add/update SharePoint list data through WebAPI

Tarun Kumar Chatterjee
 
Net – Technology Specialist
March 29, 2016
 
Rate this article
 
Views
10485

In my previous article I created a WebAPI to fetch/add/update SharePoint Employee list data. Here we will be creating a SharePoint custom WebPart which will call the WebAPI to perform the Read/Write operations.

Assume that the WebAPI & the SharePoint WebPart we are going to create will be hosted in different servers, so surely it would be a cross domain WebAPI call. We have already Enables the Cors to the WebAPI level to handle the cross domain call properly.

Let’s first create a Sharepoint Sanbox solution named as “SandBoxKendoEmployeee”

clip_image001

Give site collection name & Finish.

clip_image003

Add a Visual Webpart within the solution named as “EmployeeVisualWebpart”

Check the Feature, it should be added properly within the items in the feature

clip_image005

Select the solution, Press F4 & check if the Site URL is pointing properly to the site collection we would like to deploy this sandbox solution

clip_image007

Rebuild the solution & deploy

Now, create a SharePoint test page named as “TestKendoEmployee” & add the deployed WebPart to display the Employee List data

In the EmployeeVisualWebpart.ascx.cs add the following line of codes

 using System;
 using System.ComponentModel;
 using System.Web.UI.WebControls.WebParts;
 
 namespace SandBoxKendoEmployeee.EmployeeVisualWebpart
 {
     [ToolboxItemAttribute(false)]
     public partial class EmployeeVisualWebpart : WebPart
     {
         // Uncomment the following SecurityPermission attribute only when doing Performance Profiling on a farm solution
         // using the Instrumentation method, and then remove the SecurityPermission attribute when the code is ready
         // for production. Because the SecurityPermission attribute bypasses the security check for callers of
         // your constructor, it's not recommended for production purposes.
         // [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, UnmanagedCode = true)]
         public EmployeeVisualWebpart()
         {
         }
 
         protected override void OnInit(EventArgs e)
         {
             base.OnInit(e);
             InitializeControl();
         }
 
         protected void Page_Load(object sender, EventArgs e)
         {
         }
 
         protected override void Render(System.Web.UI.HtmlTextWriter writer)
         {
 
             writer.Write(BindScript("/sites/Site1/Style Library/KendoScripts/JS/jquery-1.10.2.min.js"));
             writer.Write(BindScript("/sites/Site1/Style Library/KendoScripts/JS/jquery.min.js"));
             writer.Write(BindScript("/sites/Site1/Style Library/KendoScripts/JS/kendo.all.min.js"));
             writer.Write(BindScript("/sites/Site1/Style Library/KendoScripts/JS/jszip.min.js"));
 
             writer.Write(BindStyle("/sites/Site1/Style Library/KendoScripts/CSS/kendo.common.min.css"));
             writer.Write(BindStyle("/sites/Site1/Style Library/KendoScripts/CSS/kendo.rtl.min.css"));
             writer.Write(BindStyle("/sites/Site1/Style Library/KendoScripts/CSS/kendo.default.min.css"));
             writer.Write(BindStyle("/sites/Site1/Style Library/KendoScripts/CSS/kendo.dataviz.min.css"));
             writer.Write(BindStyle("/sites/Site1/Style Library/KendoScripts/CSS/kendo.dataviz.default.min.css"));
             writer.Write(BindStyle("/sites/Site1/Style Library/KendoScripts/CSS/corev15_new.css"));
             base.Render(writer);
         }
 
         private string BindStyle(string StyleUrl)
         {
             StyleUrl = Microsoft.SharePoint.Utilities.SPUrlUtility.CombineUrl("Domain URL", StyleUrl);
             return string.Format(@"<link rel=""stylesheet"" href=""{0}"" type=""text/css"" />", StyleUrl);
 
         }
 
         private string BindScript(string ScriptUrl)
         {
             ScriptUrl = Microsoft.SharePoint.Utilities.SPUrlUtility.CombineUrl("Domain URL", ScriptUrl);
             return string.Format(@"<script type=""text/javascript"" src=""{0}""></script>", ScriptUrl);
         }
     }
 }
 

Downloaded the Kendo scripts/CSS/Images from http://www.telerik.com/download/kendo-ui site.

I have referred the following article: https://www.sharepointpals.com/post/How-to-Upload-the-JavaScript-Files-into-SharePoint-2013-Using-PowerShell, to upload the JS/CSS/Images within the SharePoint StyleLibrary

To download custom scripts in a SharePoint visual WebPart we will have to write the script data in Render method.

In the EmployeeVisualWebpart.ascx keep the default added code as it is & add the below pieces after that

 <div id="example">
     <div id="grid"></div>
     <script type="text/javascript">
 
         var mmsdata = {};
         var baseUrl = "http://Domain URL/WebAPITest/api/Employee";
 
         $(document).ready  (function () {
             $.ajax({
                 url: baseUrl + "/GetMMSdata",
                 type: 'GET',
                 dataType: 'jsonp',
                 contentType: "application/json",
                 async: false,
                 success: function (data) {
                     mmsdata = $.parseJSON(data);
                     var dataSource = new kendo.data.DataSource({
                         transport: {
                             read: {
                                 url: baseUrl + "/GetEmployee/all",
                                 dataType: "jsonp",
                                 contentType: "application/json",
                                 type: "GET"
                             },
 
                             update: {
 
                                 url: baseUrl + "/",
                                 dataType: "json",
                                 contentType: "application/json",
                                 complete: function (e) {
 
                                     $("input[data-role='autocomplete'][data-text-field='EmployeeName']").data("kendoAutoComplete").dataSource.data($("#grid").data("kendoGrid").dataSource.data());
                                     $("input[data-role='autocomplete'][data-text-field='EmployeeAddress']").data("kendoAutoComplete").dataSource.data($("#grid").data("kendoGrid").dataSource.data());
 
 
                                 },
                                 contentType: "application/json",
                                 type: "PUT"
                             },
                         create: {
                             url: baseUrl ,
                             type: "POST",
                             contentType: "application/json",
                             dataType: "json",
                             complete: function (e) {
                                 $("input[data-role='autocomplete'][data-text-field='EmployeeName']").data("kendoAutoComplete").dataSource.data($("#grid").data("kendoGrid").dataSource.data());
                                 $("input[data-role='autocomplete'][data-text-field='EmployeeAddress']").data("kendoAutoComplete").dataSource.data($("#grid").data("kendoGrid").dataSource.data());
                                
                             }
                         },
                         parameterMap: function (params, operation) {
                            
                             if (operation == "create") {
                                 $.each(params.models, function (index, element) {
                                     params.models[index].ID = 0;
                                 });
                                            
                             }                                       
                             if (operation !== "read" && params.models) {   
                                 return kendo.stringify(params.models);                                       
                             }
                             }
                             
                         },
                         batch: true,
                         pageSize: 30,
                         schema: {
                             model: {
                                 id: "ID",
                                 fields: {
                                     DateCreated: { type: "date", editable: false },
                                     DOB: { type: "date", editable: true },
                                     EmployeeAddress: { type: "string", editable: true },
                                     EmployeeName: { type: "string", editable: true, validation: { required: true } },
                                     MMSGUid: { field: "MMSGUid", type: "string" },
                                     Title: { type: "string", editable: true, validation: { required: true } },
                                     Manager: { type: "string", editable: true },
                                     UserName: { type: "string", editable: false }
 
                                 }
                             },
                             data: "Data",
                             parse: function (response) {
                                 $.each(response, function (idx1, elem1) {
                                     if (elem1 != null) {
                                         $.each(elem1, function (idx2, elem2) {
                                             try {
                                                 if (elem2.DateCreated !== null) {
                                                     var pDate = elem2.DateCreated;
                                                     elem2.DateCreated = pDate;
                                                     elem2.DateCreated.setHours(0);
                                                     elem2.DateCreated.setMinutes(0);
                                                 }
                                             }
                                             catch (ex) {
 
                                             }
                                         });
                                     }
                                 });
                                 return response;
                             },
                             total: function (result) {
                                 var data = this.data(result);
                                 return data ? data.length : 0;
                             }
                         }
                     });
                     $("#grid").kendoGrid({
 
                         dataSource: dataSource,
                         height: 550,
                         filterable: {
                             mode: "row"
                         },
                         sortable: true,
                         columns:
                         [
                             {
                                 field: "MMSGUid",
                                 title: "MMSData",
                                 width: 200,
                                 filterable: {
 
                                     cell: {
                                         template: function (input) {
                                             input.element.kendoDropDownList({
                                                 dataSource: $.parseJSON('[' + JSON.stringify(mmsdata).replace("[", "").replace("]", "") + ']'),
                                                 dataTextField: "Name",
                                                 valuePrimitive: true,
                                                 dataValueField: "ID",
                                                 optionLabel: {
                                                     Name: "--Select Value--",
                                                     ID: "ID"
                                                 }
                                             });
                                         },
                                         showOperators: false
                                     }
                                 },
                                 template: "#= getTermName(MMSGUid) #",
                                 editor: function (container, options) {
                                     $('<input required data-text-field="Name" data-value-field="ID" data-bind="value:' + options.field + '"/>')
                                        .appendTo(container)
                                        .kendoDropDownList({
                                            autoBind: false,
                                            dataSource: mmsdata,
                                            optionLabel: {
                                                Name: "--Select Value--",
                                                ID: "ID"
                                            }
 
                                        });
                                 },
                                 headerAttributes: { style: "background-color:#005137;font-weight:bold;font-size:1.1em;" },
                                 headerTemplate: "<a class='k-link' style='color:#ffffff' href='#' tabindex='-1'>MMSData</a>"
                             },
                              {
                                  field: "Title",
                                  title: "Title",
                                  width: 150,
                                  filterable: {
                                      cell: {
                                          enabled: true,
                                          operator: "contains"
                                      },
                                      mode: "row"
                                  },
                                  editor: function serviceItemAutoCompleteEditor(container, options) {
                                      $('<input data-text-field="Title" data-value-field="Title" data-bind="value:' + options.field + '"/>')
                                      .appendTo(container)
                                      .kendoAutoComplete({
                                          //autoBind: false,
                                          suggest: true,
                                          placeholder: "Select an item",
                                          filter: "contains",
                                          index: 1,
                                          minLength: 1,
                                          dataSource: {
                                              data: $("#grid").data("kendoGrid").dataSource.data()
                                          }
                                      })
                                  },
                                  headerAttributes: { style: "background-color:#005137;font-weight:bold;font-size:1.1em;" },
                                  headerTemplate: "<a class='k-link' style='color:#ffffff' href='#' tabindex='-1'>Title</a>"
 
                              },
                             
 
                     {
                         field: "EmployeeName",
                         title: "Employee Name",
                         width: 200,
                         filterable: {
                             cell: {
                                 enabled: true,
                                 operator: "contains"
                             },
                             mode: "row"
                         },
                         editor: function serviceItemAutoCompleteEditor(container, options) {
                             $('<input data-text-field="EmployeeName" data-value-field="EmployeeName" data-bind="value:' + options.field + '"/>')
                             .appendTo(container)
                             .kendoAutoComplete({
                                 //autoBind: false,
                                 suggest: true,
                                 placeholder: "Select an item",
                                 filter: "contains",
                                 index: 1,
                                 minLength: 1,
                                 dataSource: {
                                     data: $("#grid").data("kendoGrid").dataSource.data()
                                 }
                             })
                         },
                         headerAttributes: { style: "background-color:#005137;font-weight:bold;font-size:1.1em;" },
                         headerTemplate: "<a class='k-link' style='color:#ffffff' href='#' tabindex='-1'>Employee Name</a>"
                     },
                     {
                         field: "EmployeeAddress",
                         title: "Employee Address",
                         width: 200,
                         filterable: {
                             cell: {
                                 enabled: true,
                                 operator: "contains"
                             },
                             mode: "row"
                         },
                         editor: function serviceItemAutoCompleteEditor(container, options) {
                             $('<input data-text-field="EmployeeAddress" data-value-field="EmployeeAddress" data-bind="value:' + options.field + '"/>')
                             .appendTo(container)
                             .kendoAutoComplete({
                                 //autoBind: false,
                                 suggest: true,
                                 placeholder: "Select an item",
                                 filter: "contains",
                                 index: 1,
                                 minLength: 1,
                                 dataSource: {
                                     data: $("#grid").data("kendoGrid").dataSource.data()
                                 }
                             })
                         },
                         headerAttributes: { style: "background-color:#005137;font-weight:bold;font-size:1.1em;" },
                         headerTemplate: "<a class='k-link' style='color:#ffffff' href='#' tabindex='-1'>Employee Address</a>"
                     
                     },
                     
                     
                     {
                         field: "DOB",
                         title: "DOB",
                         filterable: {
                             cell: {
 
                                 template: function (args) {
 
                                     args.element.kendoDatePicker({
                                         format: "{0:dd-MM-yyyy}"
 
                                     });
                                 }
                             }
                         },
 
                         format: "{0:dd-MM-yyyy}",
                         groupable: false,
                         width: 150,
                         headerAttributes: { style: "background-color:#005137;font-weight:bold;font-size:1.1em;" },
                         headerTemplate: "<a class='k-link' style='color:#ffffff' href='#' tabindex='-1'>DOB</a>"
                     },
                       {
                           field: "Manager",
                           title: "Manager",
                           width: 200,
                           filterable: {
                               cell: {
                                   enabled: true,
                                   operator: "contains"
                               },
                               mode: "row"
                           },
                           editor: function serviceItemAutoCompleteEditor(container, options) {
                               $('<input data-text-field="Manager" data-value-field="Manager" data-bind="value:' + options.field + '"/>')
                               .appendTo(container)
                               .kendoAutoComplete({
                                   //autoBind: false,
                                   suggest: true,
                                   placeholder: "Select an item",
                                   filter: "contains",
                                   index: 1,
                                   minLength: 1,
                                   dataSource: {
                                       data: $("#grid").data("kendoGrid").dataSource.data()
                                   }
                               })
                           },
                           headerAttributes: { style: "background-color:#005137;font-weight:bold;font-size:1.1em;" },
                           headerTemplate: "<a class='k-link' style='color:#ffffff' href='#' tabindex='-1'>Manager</a>"
                       },
                     {
                         field: "DateCreated",
                         title: "Date Created",
                         filterable: {
                             cell: {
 
                                 template: function (args) {
 
                                     args.element.kendoDatePicker({
                                         format: "{0:dd-MM-yyyy}"
 
                                     });
                                 }
                             }
                         },
 
                         format: "{0:dd-MM-yyyy}",
                         groupable: false,
                         width: 150,
                         headerAttributes: { style: "background-color:#005137;font-weight:bold;font-size:1.1em;" },
                         headerTemplate: "<a class='k-link' style='color:#ffffff' href='#' tabindex='-1'>Date Created</a>"
                     }
                         ],
 
                         navigatable: true,
                         pageable: true,
                         height: 550,
 
                         toolbar: ["create", "save", "cancel",
                             { name: "filtertoday", text: "Today" },
                             {
                                 name: "filterweek", text: "This Week"
 
                             },
                             "excel",
                             { name: "filterall", text: "All Items" }
                         ],
                         excel: {
                             proxyURL: baseUrl + "/KendoSave",
                             fileName: "Kendo UI Grid Export.xlsx",
                             allPages: true
                         },
                         excelExport: function (e) {
                             var rows = e.workbook.sheets[0].rows;
                             for (var ri = 0; ri < rows.length; ri++) {
 
                                 var cell = rows[ri].cells[0];
                                 if (cell.value != "" && rows[ri].type == "data") {
                                     cell.value = getTermName(cell.value);
                                     // Set the alignment
                                     cell.hAlign = "left";
                                 }
                             }
                         },
                         editable: true
                     });
                 },
                 error: function (x, y, z) {
                     alert(JSON.stringify(x) + 'n' + JSON.stringify(y) + 'n' + JSON.stringify(z));
                 },
 
             });
             $(document).on('click', '.k-grid-filtertoday', function () {
                 var grid = $("#grid").data("kendoGrid");
                 grid.dataSource.page(1);
                 //grid.dataSource.read({ "FilterSpan": 'today' });
                 grid.dataSource.transport.options.read.url = baseUrl + "/GetEmployee/today";
                 grid.dataSource.read()
                 grid.refresh();
                 $('.k-grid-filtertoday').css('background-color', '#7EA700');
             });
 
 
             $(document).on('click', '.k-grid-filterweek', function () {
                 var grid = $("#grid").data("kendoGrid");
                 grid.dataSource.page(1);
                 //grid.dataSource.read({ "FilterSpan": 'week' });
                 grid.dataSource.transport.options.read.url = baseUrl + "/GetEmployee/week";
                 grid.dataSource.read()
                 grid.refresh();
                 $('.k-grid-filterweek').css('background-color', '#7EA700');
             });
 
 
             $(document).on('click', '.k-grid-filterall', function () {
                 var grid = $("#grid").data("kendoGrid");
                 grid.dataSource.page(1);
                 //grid.dataSource.read({ "FilterSpan": 'all' });
                 grid.dataSource.transport.options.read.url = baseUrl + "/GetEmployee/all";
                 grid.dataSource.read()
                 grid.refresh();
                 $('.k-grid-filterall').css('background-color', '#7EA700');
             });
 
             $(document).on('click', '.k-grid-save-changes', function () {
 
                 var grid = $("#grid").data("kendoGrid");
                 var errorMsg = "";
 
                 for (var cntItem = 0; cntItem < grid._data.length; cntItem++) {
 
                     if (grid._data[cntItem].ID == "") {
                         if (grid._data[cntItem].MMSGUid == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "Please select Term. n";
                         }
                         if (grid._data[cntItem].EmployeeName == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "Employee Name is required. n";
                         }
                         if (grid._data[cntItem].EmployeeAddress == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "Employee Address is required. n";
                         }
                         if (grid._data[cntItem].DOB == "" && grid._data[cntItem].ID == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "DOB is required. n";
                         }
                     }
                 }
                 if (errorMsg != "") {
                     alert(errorMsg);
                     return false;
                 }
             });
         });
        
         function getTermName(id) {
 
             for (var idx = 0, length = mmsdata.length; idx < length; idx++) {
                 if (mmsdata[idx].ID === id) {
                     return mmsdata[idx].Name;
                 }
             }
         }
         
 
     </script>
 </div>
 

Here Kendo grid is calling WebAPI url : http://DomainURL/WebAPITest/api/Employee to fetch/add/edit the Employee list data. In the Kendo Grid we tried here to merge both batch editing & row filtering functionalities. Also, we have some more custom functionality like Filter Today’s data, Weekly data & all data, custom validation, auto complete text box & dropdown list at the time of etc.

Now rebuild the solution and deploy

Refresh the SharePoint test page where we have added the WebPart

clip_image009

Here are some resolutions of complex issues I faced & custom functionalities I did:

Issue 1: After adding or editing the record the filter autocomplete text boxes were not refreshing, we added the below code to refresh the autocomplete textboxes after creating or editing the record

    complete: function (e) {
 
                                     $("input[data-role='autocomplete'][data-text-field='EmployeeName']").data("kendoAutoComplete").dataSource.data($("#grid").data("kendoGrid").dataSource.data());
                                     $("input[data-role='autocomplete'][data-text-field='EmployeeAddress']").data("kendoAutoComplete").dataSource.data($("#grid").data("kendoGrid").dataSource.data());
 
 
                                 }
 
 

Issue 2: In different time zone the datetime picker filter was not filtering the data properly because DateCreated values were populated with time whereas selected values from a datetime filter were with default time. So, as a solution what I did, parse the DateCreated values with default time, so that both filter & grid will be in same time format. Here is the code:

 parse: function (response) {
                                 $.each(response, function (idx1, elem1) {
                                     if (elem1 != null) {
                                         $.each(elem1, function (idx2, elem2) {
                                             try {
                                                 if (elem2.DateCreated !== null) {
                                                     var pDate = elem2.DateCreated;
                                                     elem2.DateCreated = pDate;
                                                     elem2.DateCreated.setHours(0);
                                                     elem2.DateCreated.setMinutes(0);
                                                 }
                                             }
                                             catch (ex) {
 
                                             }
                                         });
                                     }
                                 });
                                 return response;
 });
 

Issue 3: At the time of editing the record & click on Save Changes the validation was not working properly. So I added custom validation

 $(document).on('click', '.k-grid-save-changes', function () {
 
                 var grid = $("#grid").data("kendoGrid");
                 var errorMsg = "";
 
                 for (var cntItem = 0; cntItem < grid._data.length; cntItem++) {
 
                     if (grid._data[cntItem].ID == "") {
                         if (grid._data[cntItem].MMSGUid == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "Please select Term. n";
                         }
                         if (grid._data[cntItem].EmployeeName == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "Employee Name is required. n";
                         }
                         if (grid._data[cntItem].EmployeeAddress == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "Employee Address is required. n";
                         }
                         if (grid._data[cntItem].DOB == "" && grid._data[cntItem].ID == "") {
                             errorMsg = errorMsg + (cntItem + 1).toString() + ". " + "DOB is required. n";
                         }
                     }
                 }
                 if (errorMsg != "") {
                     alert(errorMsg);
                     return false;
                 }
             });
 

Custom functionality1: Todays data filter issue resolved by following code:

 $(document).on('click', '.k-grid-filtertoday', function () {
                 var grid = $("#grid").data("kendoGrid");
                 grid.dataSource.page(1);
                 //grid.dataSource.read({ "FilterSpan": 'today' });
                 grid.dataSource.transport.options.read.url = baseUrl + "/GetEmployee/today";
                 grid.dataSource.read()
                 grid.refresh();
                 $('.k-grid-filtertoday').css('background-color', '#7EA700');
             });
 

In WebAPI based on the method parameter we are filtering the data. Similarly for the Weekly data & All items.

Custom functionality2: Merge row editing & row filtering functionality achieved by following code:

 filterable: {
                             mode: "row"
                         }
 

Also, whatever I have written within filterable: {cell: {}}

Custom functionality3: Country filter should have to be a dropdown, here is the code:

 filterable: {
 
                                     cell: {
                                         template: function (input) {
                                             input.element.kendoDropDownList({
                                                 dataSource: $.parseJSON('[' + JSON.stringify(mmsdata).replace("[", "").replace("]", "") + ']'),
                                                 dataTextField: "Name",
                                                 valuePrimitive: true,
                                                 dataValueField: "ID",
                                                 optionLabel: {
                                                     Name: "--Select Value--",
                                                     ID: "ID"
                                                 }
                                             });
                                         },
                                         showOperators: false
                                     }
 

Hope this article helped people to understand about the custom webpart and leveraging WebAPI as the data source.

Happy Coding

Tarun Kumar Chatterjee

Author Info

Tarun Kumar Chatterjee
 
Net – Technology Specialist
 
Rate this article
 
Tarun has been working in IT Industry for over 12+ years. He holds a B-tech degree. He is passionate about learning and sharing the tricks and tips in Azure, .Net ...read more
 

Create a WebAPI to Add/Edit/Delete the data from a SharePoint List

Tarun Kumar Chatterjee
 
Net – Technology Specialist
 
Rate this article
 
Views
22424

Let’s first create a SharePoint list will have the following columns

clip_image001

In the list Title, Modified, Created, Created By & Modified By are the defaults, remaining site columns we have created only.

Now we will be creating few termsets that will be used by MMSData site column

Go to SiteSettings — > Term Store Management

Right Click on the Managed Metadata Service and create a new group named as “Test”, set the Group Managers & Contributors properly.

Right Click on the “Test” and Create New Term Set named as “TestTermSet”

Right click on the TestTermSet and Create Term named as “Term1”, “Term2” & “Term3”.

clip_image003

So, we are ready with the SharePoint list creation, will add some more data on the list

clip_image004

Now we will be creating an Asp.Net Web Application project named as “EmployeeWebAPI”

Select WebAPI & OK.

We will be fetching the data by using CSOM so, adding the references Mocrosoft.SharePoint.Client, Microsoft.SharePoint.Client.Runtime & Microsoft.SharePoint.Client.Taxonomy

To install System.Web.MVC in to our project need to execute the command

PM>Install-Package Microsoft.AspNet.Mvc -Version 5.0.0

Create a Model class named as “EmployeeModel.cs”, below is the code:

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 
 namespace EmployeeWebAPITest.Models
 {
     public class EmployeeModel
     {
         public int ID { get; set; }
         public DateTime? DateCreated { get; set; }
         public DateTime DOB { get; set; }
         public string EmployeeAddress { get; set; }
         public string EmployeeName { get; set; }
         public string Title { get; set; }
         public string UserName { get; set; }
         public string Manager { get; set; }
         public string MMSData { get; set; }
         public string MMSName { get; set; }
         public string MMSGUid { get; set; }
 
     }
 }
 

Create a Model class to fetch Term data named as “MMSLookup.cs”, below is the code:

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 
 namespace EmployeeWebAPITest.Models
 {
     public class MMSLookup
     {
         public string ID { get; set; }
         public string Name { get; set; }
     }
 }
 

Create another class within the Model folder named as “JsonpResult”, below is the code:

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Web;
 using System.Web.Http.Results;
 using System.Web.Mvc;
 using System.Web.Script.Serialization;
 
 namespace EmployeeWebAPITest.Models
 {
     public class JsonpResult : JsonResult
     {
          public string CallbackFunction { get; set; }
         public Encoding ContentEncoding { get; set; }
         public string ContentType { get; set; }
         public object Data { get; set; }
 
         public JsonpResult(object data) : this(data, null) { }
         public JsonpResult(object data, string callbackFunction)
         {
             Data = data;
             CallbackFunction = callbackFunction;
         }
 
 
         public override void ExecuteResult(ControllerContext context)
         {
             if (context == null) throw new ArgumentNullException("context");
 
             HttpResponseBase response = context.HttpContext.Response;
 
             response.ContentType = string.IsNullOrEmpty(ContentType) ? "application/x-javascript" : ContentType;
 
             if (ContentEncoding != null) response.ContentEncoding = ContentEncoding;
 
             if (Data != null)
             {
                 HttpRequestBase request = context.HttpContext.Request;
 
                 var callback = CallbackFunction ?? request.Params["callback"] ?? "callback";
                 var serializer = new JavaScriptSerializer();
                 response.Write(callback.ToString() + "(" + serializer.Serialize(Data).ToString() + ")");
 
             }
         }
     }
 }
 

For JavaScriptSerializer class you need to include System.Web.Extensions

Create another class named as “JsonpMediaTypeFormatter” within the Model folder, below is the code:

 using Newtonsoft.Json.Converters;
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Net.Http.Formatting;
 using System.Net.Http.Headers;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Web;
 using System.Web.Http;
 using System.Web.Script.Serialization;
 
 namespace EmployeeWebAPITest.Models
 {
     /// <summary>
     /// Handles JsonP requests when requests are fired with text/javascript
     /// </summary>
     public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
     {
         private string callbackQueryParameter;
 
         public JsonpMediaTypeFormatter()
         {
             SupportedMediaTypes.Add(DefaultMediaType);
             SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
 
             MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", DefaultMediaType));
         }
 
         public string CallbackQueryParameter
         {
             get { return callbackQueryParameter ?? "callback"; }
             set { callbackQueryParameter = value; }
         }
 
         public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
         {
             string callback;
 
             if (IsJsonpRequest(out callback))
             {
                 return Task.Factory.StartNew(() =>
                 {
                     var writer = new StreamWriter(stream);
                     writer.Write(callback + "(");
                     writer.Flush();
 
                     base.WriteToStreamAsync(type, value, stream, content, transportContext).Wait();
 
                     writer.Write(")");
                     writer.Flush();
                 });
             }
             else
             {
                 return base.WriteToStreamAsync(type, value, stream, content, transportContext);
             }
         }
 
 
         private bool IsJsonpRequest(out string callback)
         {
             callback = null;
 
             if (HttpContext.Current.Request.HttpMethod != "GET")
                 return false;
 
             callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];
 
             return !string.IsNullOrEmpty(callback);
         }
     }
 }
 
 

In Global.asax.cs within Application_Start() method add the below piece of code :

 GlobalConfiguration.Configure(WebApiConfig.Register);
 GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter());            GlobalConfiguration.Configuration.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
 

Now create an employee controller named as “EmployeeController”

 using EmployeeWebAPITest.Models;
 using Microsoft.SharePoint.Client;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Web.Http;
 using Newtonsoft.Json;
 using System.Web.Http.Cors;
 
 namespace EmployeeWebAPITest.Controllers
 {
     public class EmployeeController : ApiController
     {
         public HttpResponseMessage Options()
         {
             return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
         }
 
         [HttpGet]
         public JsonpResult GetEmployee(string filterSpan)
         {
             try
             {
                 using (ClientContext clientContext = new ClientContext("Site Collection URL"))
                 {
                     //The default network credential is nothing but the AppPool Account
                     clientContext.Credentials = CredentialCache.DefaultNetworkCredentials;
                     List<EmployeeModel> activities = null;
                     DataMangager objDataMangager = new DataMangager();
                     activities = objDataMangager.GetEmployeetDataFromHost(clientContext, filterSpan);
                     return new JsonpResult(activities.OrderBy(a => a.DateCreated).Reverse());
                 }
             }
             catch (Exception ex)
             {
                 return null;
             }
 
         }
         [HttpGet]
         public string GetMMSdata()
         {
 
             List<MMSLookup> terms = new List<MMSLookup>();
             try
             {
                 using (ClientContext clientContext = new ClientContext("Site Collection URL"))
                 {
                     //The default network credential is nothing but the AppPool Account
                     clientContext.Credentials = CredentialCache.DefaultNetworkCredentials;
                     DataMangager objDataMangager = new DataMangager();
                     terms = objDataMangager.GetTerms("TestTermSet", clientContext);
                 }
 
                 if (terms == null || terms.Count == 0) { throw new Exception("No terms fetched."); }
                 return JsonConvert.SerializeObject(terms);
             }
             catch (Exception ex)
             {
                 throw new Exception("Error fetching Terms." + ex.Message);
             }
         }
         public JsonpResult PostEmployee(List<EmployeeModel> lstClientActivity)
         {
             try
             {
                 List<Models.EmployeeModel> activities = null;
                 using (ClientContext clientContext = new ClientContext("Site Collection URL"))
                 {
                     //The default network credential is nothing but the AppPool Account
                     clientContext.Credentials = CredentialCache.DefaultNetworkCredentials;
                     DataMangager objDataMangager = new DataMangager();
                     activities = objDataMangager.AddEmployeeDataToHostBatch(clientContext, lstClientActivity, string.Empty);
                 }
                 return new JsonpResult(activities);
             }
             catch (Exception ex)
             {
                 throw new Exception("Error in create." + ex.Message);
             }
         }
 
         public JsonpResult PutEmployee(List<EmployeeModel> lstClientActivity)
         {
 
             var entities = new List<Models.EmployeeModel>();
             List<Models.EmployeeModel> activities = null;
             try
             {
                 using (ClientContext clientContext = new ClientContext("Site Collection URL"))
                 {
                     //The default network credential is nothing but the AppPool Account
                     clientContext.Credentials = CredentialCache.DefaultNetworkCredentials;
                     DataMangager objDataMangager = new DataMangager();
                     activities = objDataMangager.EditEmployeeDataOnHostBatch(clientContext, lstClientActivity, string.Empty);
                 }
                 return new JsonpResult(activities);
             }
             catch (Exception ex)
             {
                 throw new Exception("Error in edit." + ex.Message);
             }
         }
     }
 }
 

EmployeeController.cs is referring to DataManager class, here is the code snippet of DataManager class.

 using EmployeeWebAPITest.Models;
 using Microsoft.SharePoint.Client;
 using Microsoft.SharePoint.Client.Taxonomy;
 using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Net;
 using System.Web;
 using System.Xml;
 
 
 namespace EmployeeWebAPITest
 {
     public class DataMangager
     {        
         public  List<EmployeeModel> EditEmployeeDataOnHostBatch(ClientContext CSOMcontext, List<Models.EmployeeModel> input, string empname)
         {
             
 
             List<EmployeeModel> lstEmployeeModel = new List<EmployeeModel>();
             EmployeeModel objEmployeeModel = new EmployeeModel();
             if (input.Count < 1)
             {
                 throw new Exception("No records has been updated.");
             }
             Dictionary<string, string> columnNames = new Dictionary<string, string>();
             List list = null;
             string EmployeeDataListName = "Employee";
             
 
             try
             {
                 
                 list = CSOMcontext.Web.Lists.GetByTitle(EmployeeDataListName);
                 CSOMcontext.Load(list);
                 CSOMcontext.ExecuteQuery();
 
                 BuildColumnNameLookup(CSOMcontext, columnNames, list);
 
                 foreach (var item in input)
                 {
                     objEmployeeModel = new EmployeeModel();
                     ListItem oListItem;
                     oListItem = list.GetItemById(item.ID);
                     CSOMcontext.ExecuteQuery();
 
                     objEmployeeModel.ID = item.ID;
 
                     if (item.Manager != null)
                     {
                         User userTest = CSOMcontext.Web.EnsureUser(item.Manager);
                         CSOMcontext.Load(userTest);
                         CSOMcontext.ExecuteQuery();                        
                         objEmployeeModel.Manager = item.Manager;
                         oListItem[columnNames[Constants.Names.ManagerColumnName]] = userTest.Id.ToString() + ";#" + userTest.LoginName.ToString();
                         
                     }
 
                     if (item.Title != null)
                     {
                         oListItem[columnNames[Constants.Names.TitleColumnName]] = item.Title;
                         objEmployeeModel.Title = item.Title;
                     }
 
                     if (item.EmployeeName != null)
                     {
                         oListItem[columnNames[Constants.Names.EmployeeNameColumnName]] = item.EmployeeName;
                         objEmployeeModel.EmployeeName = item.EmployeeName;
                     }
 
                     if (item.EmployeeAddress != null)
                     {
                         oListItem[columnNames[Constants.Names.employeeaddressColumnName]] = item.EmployeeAddress;
                         objEmployeeModel.EmployeeAddress = item.EmployeeAddress;
                     }
                     if (item.DOB != null)
                     {
                         oListItem[columnNames[Constants.Names.dobColumnName]] = item.DOB;
                         objEmployeeModel.DOB = item.DOB;
                     }
                     
                     if (item.MMSData != null)
                     {
                         oListItem[columnNames[Constants.Names.MMSColumnName]] = string.Format("-1;#{1}|{0}", item.MMSGUid.Split((new string[] { "##" }), StringSplitOptions.None)[1], item.MMSName);
                         objEmployeeModel.MMSData = item.MMSData;
                         objEmployeeModel.MMSName = item.MMSName;
                         objEmployeeModel.MMSGUid = item.MMSGUid.Split((new string[] { "##" }), StringSplitOptions.None)[1];
                     }
 
                     oListItem.Update();
                     CSOMcontext.ExecuteQuery();
                     if (item.MMSData != null)
                     {
                         objEmployeeModel.MMSGUid = item.MMSGUid;
                     }
                     objEmployeeModel.UserName = item.UserName;
                     objEmployeeModel.DateCreated = item.DateCreated;
                     lstEmployeeModel.Add(objEmployeeModel);
                 }
 
 
             }
             catch (Exception ex)
             {
                 throw new Exception("Failed to update record." + ex.Message);
             }
             return lstEmployeeModel;
         }       
         public  List<EmployeeModel> AddEmployeeDataToHostBatch(ClientContext CSOMcontext, List<Models.EmployeeModel> input, string empname)
         {
             string EmployeeDataListName = "Employee";
             
             Dictionary<string, string> columnNames = new Dictionary<string, string>();
             List list = null;
             List<EmployeeModel> lstEmployeeModel = new List<EmployeeModel>();
             EmployeeModel objEmployeeModel = new EmployeeModel();
             try
             {
                 list = CSOMcontext.Web.Lists.GetByTitle(EmployeeDataListName);
                 CSOMcontext.Load(list);
                 CSOMcontext.ExecuteQuery();
 
                 BuildColumnNameLookup(CSOMcontext, columnNames, list);
                 ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
 
                 foreach (var item in input)
                 {
                     objEmployeeModel = new EmployeeModel();
                     ListItem oListItem;
                     oListItem = list.AddItem(itemCreateInfo);
                     User userTest = CSOMcontext.Web.EnsureUser(item.Manager);
                     CSOMcontext.Load(userTest);
                     CSOMcontext.ExecuteQuery();
                                         
                     oListItem[columnNames[Constants.Names.ManagerColumnName]] =  userTest.Id.ToString() + ";#" + userTest.LoginName.ToString();
                     objEmployeeModel.Manager = item.Manager;
                     oListItem[columnNames[Constants.Names.TitleColumnName]] = item.Title;
                     objEmployeeModel.Title = item.Title;
                     oListItem[columnNames[Constants.Names.EmployeeNameColumnName]] = item.EmployeeName;
                     objEmployeeModel.EmployeeName = item.EmployeeName;
                     oListItem[columnNames[Constants.Names.employeeaddressColumnName]] = item.EmployeeAddress;
                     objEmployeeModel.EmployeeAddress = item.EmployeeAddress;
                     oListItem[columnNames[Constants.Names.dobColumnName]] = item.DOB;
                     objEmployeeModel.DOB = item.DOB;
                    
                     oListItem[columnNames[Constants.Names.MMSColumnName]] = string.Format("-1;#{1}|{0}", item.MMSGUid.Split((new string[] { "##" }), StringSplitOptions.None)[1], empname);
                     objEmployeeModel.MMSData = item.MMSData;
                     objEmployeeModel.MMSName = item.MMSName;
                     
                     objEmployeeModel.MMSGUid = item.MMSGUid.Split((new string[] { "##" }), StringSplitOptions.None)[1];
                     oListItem.Update();
                     CSOMcontext.ExecuteQuery();
                     objEmployeeModel.MMSGUid = item.MMSGUid;
                     objEmployeeModel.DateCreated = DateTime.Now;
                     //objEmployeeModel.UserName = item.UserName;
                     lstEmployeeModel.Add(objEmployeeModel);
                 }
             }
             catch (Exception ex)
             {
                 throw new Exception("Failed to add new record." + ex.Message);
             }
             return lstEmployeeModel;
         }                
         public List<EmployeeModel> GetEmployeetDataFromHost(ClientContext CSOMcontext, string filterSpan)
         {
             string EmployeeDataListName = "Employee";
 
             //Name MMSCountry Activity Status Created Created By Modified Modified By
             Dictionary<string, string> columnNames = new Dictionary<string, string>();
 
             List<EmployeeModel> results = new List<EmployeeModel>();
             List list = null;
             Web web = null;
             try
             {
                 web = CSOMcontext.Web;
                 ListCollection lists = web.Lists;
                 CSOMcontext.Load(lists);
                 CSOMcontext.ExecuteQuery();
             }
             catch (Exception ex)
             {
                 throw new Exception("Unable to access host web at " + CSOMcontext.Url + "." + ex.Message);
             }
             try
             {
                 list = web.Lists.GetByTitle(EmployeeDataListName);
                 CSOMcontext.Load(list);
                 CSOMcontext.ExecuteQuery();
             }
             catch (Exception ex)
             {
                 throw new Exception("List " + EmployeeDataListName + " not found." + ex.Message);
             }
 
             BuildColumnNameLookup(CSOMcontext, columnNames, list);
 
             string fromDateFx = DateTime.Now.AddHours(-2).ToString("yyyy-MM-ddTHH:mm:ssZ");
             string toDateFx = DateTime.Now.AddDays(1).ToString("yyyy-MM-ddTHH:mm:ssZ");
 
             if (list != null && list.ItemCount > 0)
             {
                 CamlQuery camlQuery = new Microsoft.SharePoint.Client.CamlQuery();
                 if (filterSpan == "today")
                 {
                     camlQuery = CreateCamlDateQuery(fromDateFx, toDateFx);
                 }
 
                 ListItemCollection allItems = list.GetItems(camlQuery);
                 CSOMcontext.Load(allItems, items => items.Take(500));
                 CSOMcontext.ExecuteQuery();
 
                 foreach (ListItem item in allItems)
                 {
                     EmployeeModel emp = new EmployeeModel
                     {
                         ID = item.Id,
                         Title = "unknown",
                         EmployeeAddress = "unknown",
                         DOB = DateTime.MinValue,
                         EmployeeName = "unknown",
                         Manager = "unknown",
                         MMSData = "unknown",
                         DateCreated = DateTime.Now
 
                     };
                     try
                     {
 
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.dobColumnName]))
                         {
                             emp.DOB = Convert.ToDateTime(Convert.ToDateTime(item[columnNames[Constants.Names.dobColumnName]]).ToShortDateString());
                         }
 
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.createdColumnName]))
                         {
                             emp.DateCreated = Convert.ToDateTime(Convert.ToDateTime(item[columnNames[Constants.Names.createdColumnName]]).ToShortDateString());
                         }
 
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.employeeaddressColumnName]))
                         {
                             emp.EmployeeAddress = item[columnNames[Constants.Names.employeeaddressColumnName]].ToString();
                             emp.EmployeeAddress =  emp.EmployeeAddress.Substring(emp.EmployeeAddress.IndexOf("">") + 2, (emp.EmployeeAddress.IndexOf("</div>") - 2 - emp.EmployeeAddress.IndexOf("">")));
                         }
 
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.EmployeeNameColumnName]))
                         {
                             emp.EmployeeName = item[columnNames[Constants.Names.EmployeeNameColumnName]].ToString();
                         }
 
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.TitleColumnName]))
                         {
                             emp.Title = item[columnNames[Constants.Names.TitleColumnName]].ToString();
                         }
 
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.ManagerColumnName]))
                         {
                             emp.Manager = ((FieldUserValue)item[columnNames[Constants.Names.ManagerColumnName]]).LookupValue;
                         }
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.MMSColumnName]))
                         {
                             TaxonomyFieldValue mmsfield = (TaxonomyFieldValue)item[columnNames[Constants.Names.MMSColumnName]];
                             emp.MMSData = mmsfield.Label;
                             emp.MMSGUid = emp.MMSData.Split(':').Last() + "##" + mmsfield.TermGuid;
                             emp.MMSName = emp.MMSData;
                         }
 
                         if (item.FieldValues.ContainsKey(columnNames[Constants.Names.CreatedByColumnName]))
                         {
                             emp.UserName = ((FieldUserValue)item[columnNames[Constants.Names.CreatedByColumnName]]).LookupValue;
                         }
                         results.Add(emp);
 
                     }
                     catch { }
 
                 }
             }
 
             return results;
         }
         private  CamlQuery CreateCamlDateQuery(string fromDate, string toDate)
         {
             string camlString = String.Format("{0}n{1}n{2}n{3}n{4}",
                 "<View><Query><Where> <And><Geq>",
                 "<FieldRef Name='Created'/><Value Type='DateTime'>" + fromDate + "</Value>",
                 "</Geq><Leq><FieldRef Name='Created'/>",
                 "<Value Type='DateTime'>" + toDate + "</Value>",
                 "</Leq></And></Where></Query></View>");
             return new Microsoft.SharePoint.Client.CamlQuery() { ViewXml = camlString };//IncludeTimeValue='FALSE'
         }
         public  DateTime FirstDayOfWeek(DateTime date)
         {
             DayOfWeek fdow = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
             int offset = fdow - date.DayOfWeek;
             DateTime fdowDate = date.AddDays(offset);
             return fdowDate;
         }
         public DateTime LastDayOfWeek(DateTime date)
         {
             DateTime ldowDate = FirstDayOfWeek(date).AddDays(6);
             return ldowDate;
         }
         public  List<MMSLookup> GetTerms(string termSetName, ClientContext clientContext)
         {
             List<MMSLookup> mmsdata = new List<MMSLookup>();
             TaxonomySession tSession = TaxonomySession.GetTaxonomySession(clientContext);
             TermStore ts = tSession.GetDefaultSiteCollectionTermStore();
             TermSetCollection tsets = ts.GetTermSetsByName(termSetName, 1033);
             clientContext.Load(tSession);
             clientContext.Load(ts);
             clientContext.Load(tsets);
             clientContext.ExecuteQuery();
             TermSet tset = tsets[0];
             TermCollection terms = tset.GetAllTerms();//tset.GetTerms(lmi);   //
             clientContext.Load(terms);
             clientContext.ExecuteQuery();
 
 
             if (terms != null && terms.Count() > 0)
             {
                 foreach (var t in terms)
                 {
                     string[] termbits = t.PathOfTerm.Split(';');
                     
                     {
                         MMSLookup c = new MMSLookup();
                         
                         c.Name = t.PathOfTerm.Split(';').Last();
                        
                         c.ID = c.Name + "##" + t.Id.ToString();
                         mmsdata.Add(c);
                     }
                 }
             }
             return mmsdata;
         }
         private  void BuildColumnNameLookup(ClientContext CSOMcontext, Dictionary<string, string> columnNames, List list)
         {
 
             columnNames.Add(Constants.Names.dobColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.dobColumnName));
             columnNames.Add(Constants.Names.createdColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.createdColumnName));
             columnNames.Add(Constants.Names.employeeaddressColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.employeeaddressColumnName));
             columnNames.Add(Constants.Names.EmployeeNameColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.EmployeeNameColumnName));
             columnNames.Add(Constants.Names.ManagerColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.ManagerColumnName));
             columnNames.Add(Constants.Names.TitleColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.TitleColumnName)); 
             columnNames.Add(Constants.Names.MMSColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.MMSColumnName));
             columnNames.Add(Constants.Names.CreatedByColumnName, GetCSOMcolumnRef(CSOMcontext, list, Constants.Names.CreatedByColumnName));
         }
         private  string GetCSOMcolumnRef(ClientContext ctx, List list, string displayName)
         {
             Field fld = list.Fields.GetByTitle(displayName);
             ctx.Load(fld);
             ctx.ExecuteQuery();
             return fld.InternalName;
         }
 
     }
 }
 

DataManager.cs is referring to a Constant Class; here is the code snippet

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 
 namespace EmployeeWebAPITest
 {
     public class Constants
     {
         public class Names
         {
             public const string createdColumnName = "Created";
 
             public const string dobColumnName = "DOB";
 
             public const string employeeaddressColumnName = "EmployeeAddress";
 
             public const string EmployeeNameColumnName = "EmployeeName";
 
             public const string ManagerColumnName = "Manager";
 
             public const string MMSColumnName = "MMSData";
          
             public const string TitleColumnName = "Title";
 
             public const string CreatedByColumnName = "Created By";
            
         }
     }
 }
 

Within the App_Start folder in WebApiConfig.cs class add the following piece of code

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web.Http;
 
 namespace EmployeeWebAPITest
 {
     public static class WebApiConfig
     {
         public static void Register(HttpConfiguration config)
         {
             // Web API configuration and services
 
             // Web API routes
             config.MapHttpAttributeRoutes();
 
             config.Routes.MapHttpRoute(
                name: "GetEmployee",
                routeTemplate: "api/Employee/GetEmployee/{filterSpan}",
                defaults: new { controller = "Employee", action = "GetEmployee" }
          );
 
             config.Routes.MapHttpRoute(
                 name: "DefaultApi",
                 routeTemplate: "api/{controller}/{id}",
                 defaults: new { id = RouteParameter.Optional }
             );
         }
     }
 }
 

Now build the solution & run

Here is the output of MMS data

clip_image006

Here is the output of Employee List data

clip_image008

HttpGet works fine in both IE & Chrome. The problem I have with HttpPost method in chrome but not in IE.

At the time of posting the employee data through Chrome it is throwing me the following error:

XMLHttpRequest cannot load http://<Domain Name>/WebAPITest/api/Employee/. Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://<Domain Name>’ is therefore not allowed access.

As I have mentioned in the article: https://www.sharepointpals.com/post/How-to-achieve-the-Cross-domain-WebAPI-(GetPost)-call-using-Ajax-request, we need to follow the below steps to resolve the issue

Run the below commands in Package Manager Console

PM> Install-Package NuGet.Core

PM> Update-Package Microsoft.AspNet.WebApi –reinstall

PM> Install-Package Microsoft.AspNet.WebApi.Cors.ko -Version 5.0.0

In the EmployeeController class add the following attribute:

[EnableCors(origins: "*", headers: "*", methods: "*")]

Add the following piece of the line in web.config

 <system.webServer>
     <httpProtocol>
       <customHeaders>
         <add name="Access-Control-Expose-Headers " value="WWW-Authenticate"/>
         <add name="Access-Control-Allow-Origin" value="*" />
         <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD" />
         <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
       </customHeaders>
     </httpProtocol>
   </system.webServer>
 

Now the HttpPost from Chrome will be working properly.

As this WebAPI itself becomes a very lengthy article, so planning to write to display this WebAPI data into a Kendo grid which will be hosted within a custom SharePoint WebPart .

Happy Coding

Tarun Kumar Chatterjee

Author Info

Tarun Kumar Chatterjee
 
Net – Technology Specialist
 
Rate this article
 
Tarun has been working in IT Industry for over 12+ years. He holds a B-tech degree. He is passionate about learning and sharing the tricks and tips in Azure, .Net ...read more
 

How to Invoke WebAPI using PowerShell – SharePoint 2013

Sathish Nadarajan
 
Solution Architect
February 29, 2016
 
Rate this article
 
Views
7584

I came across an interesting workaround to improve the performance of some search and webapi calls to one of our customer.

Let me explain the scenario in detail. I have a WebAPI which does the Search based on certain input parameters. This took some expensive timelines to complete the request and response. Hence, we thought of keeping all the possible inputs and the search results in a Cache.

i.e., coming up with a WarmUp WebAPI Method, which will do all the possible searches and keeps the search results in Cache Objects appropriately. So that, when the actual request comes, it will look in the cache array.

Now, the question is, how to call this WamUp method periodically, i.e., for every two hours. For that, I have come across a PowerShell Script, which will invoke the WebAPI.

 cls
 #Base url
 $urlPrefix = "http://Site/WebAPI/";
 #Endpoints
 $endpoints = @(
 #"api/Search/MyAPIMethod/Param1/Param2?callback=?", 
  "api/search/Method2?callback=?"
 );
 
 $headers = @{"Client-Token"="my-app-client-secret-token"};
 
 function Log([string] $url, $exception){
     #Create EventLog source if it doesn't exist
     $eventSource = "MyApp Job";
     if (![System.Diagnostics.EventLog]::SourceExists($eventSource)){
         New-Eventlog -LogName "Application" -Source $eventSource;
     }
 
     #Write warning to EventLog
     $message = "Call failed URL: " + $url + " Details: " + $exception;
     Write-EventLog -LogName "Application"`
      -Source $eventSource -EventId 1 -EntryType Warning -Message $message;
 }
 
 #Call each endpoint 
 foreach ($endpoint in $endpoints) {
     Write-Output -InputObject $endpoint;
 
     try {
         $response = Invoke-RestMethod -Uri ($urlPrefix + $endpoint)`
          -method GET -ContentType "application/json" -Headers $headers;
         Write-Output -InputObject $response;
     }
     catch {
         Write-Output -InputObject $_.Exception.Response.StatusCode.Value__;
         Log -url ($urlPrefix + $endpoint) -exception $_.Exception.Message;      
     }
 }
 

Now, this PS1 can be called from a BAT and that BAT will be scheduled in the Task Scheduler.

Happy Coding,

Sathish Nadarajan.

Author Info

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 15+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business ...read more
 

How to Make an Ajax Call to WebAPI from Visual WebPart in SharePoint 2013

Sathish Nadarajan
 
Solution Architect
December 9, 2015
 
Rate this article
 
Views
9878

In the recent development era, almost every customer is looking for the SandBox Solutions. In that case, we cannot use many Server Side Object Model Coding Standards. In this scenario, most of the business logics will be moved to the WebAPI and the WebParts will consume the WebAPI. In this article, let us see such a webpart, which consumes a WebAPI and renders the output from the WebAPI as a Table.

As usual, we will be creating a project and add a Visual WebPart. Not the Visual WebPart (Farm Solution Only).

clip_image002

On the ASCX file, we need to call the WebAPI.

 <script type="text/javascript">
 
      $(document).ready(function () {
 
          $.ajax({
             url: webAPIURL + '/api/MyController/' + Parameter1 + '/' + parameter2,
             type: 'GET',
             dataType: 'jsonp',
             success: function (data) {
                 if (data.length > 0) {
                     $.each(data, function (idx, elem) {
                          //Do the actual process here.. 
 //$('#areaDocTopics').append('<div class="col-xs-6 col-sm-4 col-lg-3"><a class="block block-link-hover3 text-center" href="#" onclick="CallBackSuccess('' + elem.Url + '');return false;" ><div class="block-content block-content-full bg-green"><h5><i class="fa fa-bookmark"></i>' + elem.Title + '</h5></div><div class="block-content block-content-full block-content-mini"><span class="articles"><i class="fa fa-newspaper-o"></i>' + elem.Count + '</span><span class="videos"><i class="fa fa-video-camera"></i>' + elem.Count + '</span><span class="documents"><i class="fa fa-file-text-o"></i>' + elem.Count+ '</span></div></a></div>');
                     });
                 }
                 else {
                     $('#areaDocTopics').html('<div class="alert alert-warning text-center"><h3>No data found</h3></div>');
                 }
                  
             },
             error: function (x, y, z) {
                 alert(JSON.stringify(x) + 'n' + JSON.stringify(y) + 'n' + JSON.stringify(z));
                  
             }
 
         });
     });
 </script>
 

 

 

Happy Coding,

Sathish Nadarajan.

Author Info

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 15+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business ...read more
 

How to create a SPA MVC CRUD application using WebAPI, Angular JS, bootstrap & Entity framework

Tarun Kumar Chatterjee
 
Net – Technology Specialist
November 15, 2015
 
Rate this article
 
Views
10445

In this artifact let me explain you the steps to create a SPA MVC client application which will pull the data from WebAPI using Get or Post methods. Also, I have incorporated Angular JS, now a day it is most commonly used JavaScript framework. I am trying to cover Angular functionality called as ng-view to load partial views which is similar like usercontrol we have used in Asp.net application, also have tried to resolve the issues I faced at the time of implementing.

In traditional web applications, the client (browser) communicates with server by requesting a page. The server then processes the request and sends the HTML page to the client. In subsequent interactions with the page, for example the user navigates to a link or submits a form with data, a new request is sent to the server and the flow starts again. The server processes the request and sends a new page to the browser in response to the new action requested by the client.
But in Single-Page Applications (SPAs) the entire page is loaded into the browser after the initial request, subsequent interactions take place using Ajax requests. There is no need to reload the entire page. The SPA approach reduces the time taken by the application to respond to user actions, resulting in a more fluid experience.
clip_image002
Now we will learn this by creating a sample application. In this SPA example I will do CRUD operations on the Student table using the Web API and MVC.

The below DB script will create a table with Data needs to be executed in SQL Server database.

 USE [EmpDb]
 GO
 /****** Object:  Table [dbo].[Employees]    Script Date: 10/31/2015 23:24:53 ******/
 SET ANSI_NULLS ON
 GO
 SET QUOTED_IDENTIFIER ON
 GO
 CREATE TABLE [dbo].[Employees](
 	[EmployeeId] [int] IDENTITY(1,1) NOT NULL,
 	[FirstName] [nvarchar](20) NOT NULL,
 	[LastName] [nvarchar](20) NOT NULL,
 	[Description] [nvarchar](100) NOT NULL,
 	[Salary] [real] NOT NULL,
 	[Country] [nvarchar](50) NOT NULL,
 	[State] [nvarchar](50) NOT NULL,
 	[DateofBirth] [datetime] NOT NULL,
 	[IsActive] [bit] NOT NULL,
  CONSTRAINT [PK_dbo.Employees] PRIMARY KEY CLUSTERED 
 (
 	[EmployeeId] ASC
 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
 ) ON [PRIMARY]
 GO
 
 SET IDENTITY_INSERT [dbo].[Employees] ON
 INSERT [dbo].[Employees] ([EmployeeId], [FirstName], [LastName], [Description], [Salary], [Country], [State], [DateofBirth], [IsActive]) VALUES (1, N'Tarun1', N'Chatterjee1', N'Tarun1 Chatterjee1', 99999, N'IN', N'WB', CAST(0x00008403017F260F AS DateTime), 0)
 INSERT [dbo].[Employees] ([EmployeeId], [FirstName], [LastName], [Description], [Salary], [Country], [State], [DateofBirth], [IsActive]) VALUES (2, N'Tarun2', N'Chatterjee2', N'Tarun2 Chatterjee2', 49999.28, N'IN', N'WB', CAST(0x00008128017F2610 AS DateTime), 1)
 INSERT [dbo].[Employees] ([EmployeeId], [FirstName], [LastName], [Description], [Salary], [Country], [State], [DateofBirth], [IsActive]) VALUES (3, N'Tarun3', N'Chatterjee3', N'Tarun3 Chatterjee3', 234234240, N'IN', N'WB', CAST(0x0000A4CE0130DEE0 AS DateTime), 0)
 INSERT [dbo].[Employees] ([EmployeeId], [FirstName], [LastName], [Description], [Salary], [Country], [State], [DateofBirth], [IsActive]) VALUES (6, N'Tarun4', N'Chatterjee4', N'Tarun4 Chatterjee4', 889999, N'IN', N'WB', CAST(0x0000A4CF00000000 AS DateTime), 1)
 SET IDENTITY_INSERT [dbo].[Employees] OFF
 
 

Our database is ready. Now we will have to pull the data into the WebAPI with help of Entity Framework. Let see how it would be:

First, we will open Visual Studio 2013 & then click on New Project.

clip_image004

Select Web -> ASP.NET Web Application then provide a name & then click on OK.clip_image005

Right-click on the Models Folder & then select Add — > ADO.NET Entity Data Model.

clip_image007

Give the Item name as “ManageEmployee” & then Ok

clip_image009

clip_image011

Next, select table Name & then Finish

Now we will have to take Angular JS references. So, go to Package manager console and execute the below command to install Angular JS package to your solution.

PM> Install-Package angularjs

In RouteConfig add the following code

routes.MapRoute(

name: "Default",

url: "{controller}/{action}/{id}",

defaults: new { controller = "Employee", action = "Index", id = UrlParameter.Optional }

);

Then we will have to add a Web API Controller, so right-click on the controller folder & then select Add — > Controller.

clip_image013

clip_image015

Here is the generated EmployeeAPIController class

 using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Data.Entity;
 using System.Data.Entity.Infrastructure;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Web.Http;
 using System.Web.Http.Description;
 using SpaWebApiMVC.Models;
 
 namespace SpaWebApiMVC.Controllers
 {
     public class EmployeeAPIController : ApiController
     {
         private EmpDbEntities db = new EmpDbEntities();
 
         // GET api/EmployeeAPI
         public IQueryable<Employee> GetEmployee()
         {
             return db.Employees;
         }
 
         // GET api/EmployeeAPI/5
         [ResponseType(typeof(Employee))]
         public IHttpActionResult GetEmployee(int id)
         {
             Employee employee = db.Employees.Find(id);
             if (employee == null)
             {
                 return NotFound();
             }
 
             return Ok(employee);
         }
 
         // PUT api/EmployeeAPI/5
         public IHttpActionResult PutEmployee(int id, Employee employee)
         {
             if (!ModelState.IsValid)
             {
                 return BadRequest(ModelState);
             }
 
             if (id != employee.EmployeeId)
             {
                 return BadRequest();
             }
 
             db.Entry(employee).State = EntityState.Modified;
 
             try
             {
                 db.SaveChanges();
             }
             catch (DbUpdateConcurrencyException)
             {
                 if (!EmployeeExists(id))
                 {
                     return NotFound();
                 }
                 else
                 {
                     throw;
                 }
             }
 
             return StatusCode(HttpStatusCode.NoContent);
         }
 
         // POST api/EmployeeAPI
         [ResponseType(typeof(Employee))]
         public IHttpActionResult PostEmployee(Employee employee)
         {
             if (!ModelState.IsValid)
             {
                 return BadRequest(ModelState);
             }
 
             db.Employees.Add(employee);
             db.SaveChanges();
 
             return CreatedAtRoute("DefaultApi", new { id = employee.EmployeeId }, employee);
         }
 
         // DELETE api/EmployeeAPI/5
         [ResponseType(typeof(Employee))]
         public IHttpActionResult DeleteEmployee(int id)
         {
             Employee employee = db.Employees.Find(id);
             if (employee == null)
             {
                 return NotFound();
             }
 
             db.Employees.Remove(employee);
             db.SaveChanges();
 
             return Ok(employee);
         }
 
         protected override void Dispose(bool disposing)
         {
             if (disposing)
             {
                 db.Dispose();
             }
             base.Dispose(disposing);
         }
 
         private bool EmployeeExists(int id)
         {
             return db.Employees.Count(e => e.EmployeeId == id) > 0;
         }
     }
 }
 
 

Again, right-click on the Controller folder & then select Add — > Controller.

Select MVC Controller Empty and then ADD

Give the controller name as “EmployeeController”

Now, we are ready to add a view.

clip_image017

Index.chtml Code will be like:

 @{
     ViewBag.Title = "SPA";
     Layout = "~/Views/Shared/_Layout.cshtml";
 }
 
 <link href="~/Content/bootstrap.min.css" rel="stylesheet" />
 <body data-ng-app="ApplicationModule">
     <div>
         <div>
             <div>
                 <table cellpadding="5" cellspacing="6" width="100%" style="background-color:none; border:solid 4px black;">
                     <tr>
                         <td style="border: solid 1px ; width:170px; text-align:center;" class="glyphicon glyphicon-user"><a href="#/showEmployee"> Show All</a></td>
                         <td style="border: solid 1px ; width:170px; text-align:center;" class="glyphicon glyphicon-plus"><a href="#/addEmployee"> Add New</a></td>
                         <td></td>
                     </tr>
                 </table>
             </div>
             <div>
                 <div data-ng-view></div>
             </div>
         </div>
     </div>
 
 </body>
 
 @section scripts{
     <script type="text/javascript" src="@Url.Content("~/Scripts/angular.js")"></script>
     <script type="text/javascript" src="@Url.Content("~/Scripts/angular-route.min.js")"></script>
     <script type="text/javascript" src="@Url.Content("~/CustomScripts/Module.js")"></script>
     <script type="text/javascript" src="@Url.Content("~/CustomScripts/Services.js")" ></script>
     <script type="text/javascript" src="@Url.Content("~/CustomScripts/ShowEmployeeController.js")"></script>
     <script type="text/javascript" src="@Url.Content("~/CustomScripts/AddEmployeeController.js")"></script>
     <script type="text/javascript" src="@Url.Content("~/CustomScripts/EditEmployeeController.js")"></script>
     <script type="text/javascript" src="@Url.Content("~/CustomScripts/DeleteEmployeeController.js")"></script>
 } 
 
 

Here, we will add a partial view for all the modules. So right-click on Views then selects the Employee folder.

clip_image019

ShowEmployee.chtml code will be like:

<table cellpadding="4" cellspacing="4" width="100%" style="background-color:none; border:solid 2px ; padding-top:10px;">

<thead>

<tr style="background-color:none">

<th>Employee ID</th>

<th>FirstName</th>

<th>LastName</th>

<th>Description</th>

<th>Salary</th>

<th>Country</th>

<th>State</th>

<th>DateofBirth</th>

<th>IsActive</th>

</tr>

</thead>

<tbody>

<tr data-ng-repeat="emp in Employee">

<td>{{emp.employeeId}}</td>

<td>{{emp.firstName}}</td>

<td>{{emp.lastName}}</td>

<td>{{emp.description}}</td>

<td>{{emp.Salary}}</td>

<td>{{emp.country}}</td>

<td>{{emp.state}}</td>

<td>{{emp.dateofBirth}}</td>

<td>{{emp.isActive}}</td>

<td><a ng-href="#/editEmployee/{{emp.employeeId}}" class="glyphicon glyphicon-edit">Edit</a></td>

<td><a ng-href="#/deleteEmployee/{{emp.employeeId}}" class="glyphicon glyphicon-trash">Delete</a></td>

</tr>

</tbody>

</table>

Similarly, AddEmployee.chtml code will be like:

@{

ViewBag.Title = "Add New Employee";

}

<table><tr><td height="10px"></td></tr></table>

<table cellpadding="4" cellspacing="4" width="70%" align="center" style="background-color:none;

border:solid 2px ; padding-top:20px;">

<tr>

<td colspan="3" style="background-color:gray; font-size:18pt;

font-weight:bold; height:30px; text-align:center;">

Add New Employee

</td>

</tr>

<tr>

<td style="text-align:right;">Employee ID</td>

<td><input type="text" ng-model="EmployeeId" /> </td>

</tr>

<tr>

<td style="text-align:right;">FirstName</td>

<td><input type="text" ng-model="FirstName" /> </td>

</tr>

<tr>

<td style="text-align:right;">LastName</td>

<td><input type="text" ng-model="LastName" /> </td>

</tr>

<tr>

<td style="text-align:right;">Description</td>

<td><input type="text" ng-model="Description" /> </td>

</tr>

<tr>

<td style="text-align:right;">Salary</td>

<td><input type="text" ng-model="Salary" /> </td>

</tr>

<tr>

<td style="text-align:right;">Country</td>

<td><input type="text" ng-model="Country" /> </td>

</tr>

<tr>

<td style="text-align:right;">State</td>

<td><input type="text" ng-model="State" /> </td>

</tr>

<tr>

<td style="text-align:right;">DateofBirth</td>

<td><input type="text" ng-model="DateofBirth" /> </td>

</tr>

<tr>

<td style="text-align:right;">IsActive</td>

<td><input type="text" ng-model="IsActive" /> </td>

</tr>

<tr>

<td></td>

<td>

<input type="button" value="Save" data-ng-click="save()" />

</td>

</tr>

</table>

EditEployee.chtml code will be like:

@{

ViewBag.Title = "Edit Employee";

}

<table><tr><td height="10px"></td></tr></table>

<table cellpadding="4" cellspacing="4" width="70%" align="center" style="background-color: none; border:solid 2px black; padding-top:20px;">

<tr><td colspan="3" style="background-color:none; font-size:18pt; font-weight:bold; height:30px; text-align:center;">Edit Employee</td></tr>

<tr>

<td style="text-align:right;">Employee ID</td>

<td><input type="text" ng-model="Employee.employeeId" /> </td>

</tr>

<tr>

<td style="text-align:right;">FirstName</td>

<td><input type="text" ng-model="Employee.firstName" /> </td>

</tr>

<tr>

<td style="text-align:right;">LastName</td>

<td><input type="text" ng-model="Employee.lastName" /> </td>

</tr>

<tr>

<td style="text-align:right;">Description</td>

<td><input type="text" ng-model="Employee.description" /> </td>

</tr>

<tr>

<td style="text-align:right;">Salary</td>

<td><input type="text" ng-model="Employee.salary" /> </td>

</tr>

<tr>

<td style="text-align:right;">Country</td>

<td><input type="text" ng-model="Employee.country" /> </td>

</tr>

<tr>

<td style="text-align:right;">State</td>

<td><input type="text" ng-model="Employee.state" /> </td>

</tr>

<tr>

<td style="text-align:right;">DateofBirth</td>

<td><input type="text" ng-model="Employee.dateofBirth" /> </td>

</tr>

<tr>

<td style="text-align:right;">IsActive</td>

<td><input type="text" ng-model="Employee.isActive" /> </td>

</tr>

<tr>

<td></td>

<td>

<input type="button" value="Save" ng-click="save()" />

<br />

<div>{{error}}</div>

</td>

</tr>

</table>

DeleteEmployee.chtml code will be like:

@{

ViewBag.Title = "Delete Employee";

}

<table><tr><td height="10px"></td></tr></table>

<table cellpadding="4" cellspacing="4" width="70%" align="center" style="background-color: none; border:solid 2px black; padding-top:20px;">

<tr><td colspan="3" style="background-color:none; font-size:18pt; font-weight:bold; height:30px; text-align:center;">Delete Employee</td></tr>

<tr>

<td style="text-align:right;">Employee ID</td>

<td>{{Employee.employeeId}} </td>

</tr>

<tr>

<td style="text-align:right;">FirstName</td>

<td> {{Employee.firstName}} </td>

</tr>

<tr>

<td style="text-align:right;">LastName</td>

<td>{{Employee.lastName}} </td>

</tr>

<tr>

<td style="text-align:right;">Description</td>

<td>{{Employee.description}} </td>

</tr>

<tr>

<td style="text-align:right;">Salary</td>

<td>{{Employee.salary}} </td>

</tr>

<tr>

<td style="text-align:right;">Country</td>

<td>{{Employee.country}} </td>

</tr>

<tr>

<td style="text-align:right;">State</td>

<td>{{Employee.state}} </td>

</tr>

<tr>

<td style="text-align:right;">DateofBirth</td>

<td>{{Employee.dateofBirth}} </td>

</tr>

<tr>

<td style="text-align:right;">IsActive</td>

<td>{{Employee.isActive}} </td>

</tr>

<tr>

<td></td>

<td>

<input type="button" value="Delete" ng-click="delete()" />

</td>

</tr>

</table>

Next, we will have to add the following code in EmployeeController.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

namespace SpaWebApiMVC.Controllers

{

public class EmployeeController : Controller

{

//

// GET: /Employee/

public ActionResult Index()

{

return View();

}

public ActionResult AddEmployee()

{

return PartialView("AddEmployee");

}

public ActionResult ShowEmployee()

{

return PartialView("ShowEmployee");

}

public ActionResult EditEmployee()

{

return PartialView("EditEmployee");

}

public ActionResult DeleteEmployee()

{

return PartialView("DeleteEmployee");

}

}

}

Again, we will have to add a new folder called “CustomScripts”, to the solution which will have the custom controllers for all the individual modules.

So, we need to add the following JavaScript files within “CustomScripts” folder.

  • Module.js
  • Services.js
  • ShowEmployeeController.js
  • AddEmployeeController.js
  • EditEmployeeController.js
  • DeleteEmployeeController.js

Module.JS code will be like:

var app = angular.module("ApplicationModule", ["ngRoute"]);

app.factory("ShareData", function () {

return { value: 0 }

});

//Showing Routing

app.config([‘$routeProvider’, ‘$locationProvider’, function ($routeProvider, $locationProvider) {

$routeProvider.when(‘/showEmployee’,

{

templateUrl: ‘Employee/ShowEmployee’,

controller: ‘ShowEmployeeController’

});

$routeProvider.when(‘/addEmployee’,

{

templateUrl: ‘Employee/AddEmployee’,

controller: ‘AddEmployeeController’

});

$routeProvider.when("/editEmployee/:id",

{

templateUrl: ‘Employee/EditEmployee’,

controller: ‘EditEmployeeController’

});

$routeProvider.when(‘/deleteEmployee/:id’,

{

templateUrl: ‘Employee/DeleteEmployee’,

controller: ‘DeleteEmployeeController’

});

$routeProvider.otherwise(

{

redirectTo: ‘/showEmployee’

});

//This can be used to remove # from URL

//$locationProvider.html5Mode(true).hashPrefix(‘!’)

}]);

Services.js code will be like:

app.service("SPACRUDService", function ($http) {

//Read all Employee

this.getEmployees = function () {

return $http.get("/api/EmployeeAPI");

};

//Fundction to Read Employee by Employee ID

this.getEmployee = function (id) {

return $http.get("/api/EmployeeAPI/" + id);

};

//Function to create new Employee

this.post = function (Employee) {

var request = $http({

method: "post",

url: "/api/EmployeeAPI",

data: Employee

});

return request;

};

//Edit Employee By Employee ID

this.put = function (id, Employee) {

var request = $http({

method: "put",

url: "/api/EmployeeAPI/" + id,

data: Employee

});

return request;

};

//Delete Employee By Employee ID

this.delete = function (id) {

var request = $http({

method: "delete",

url: "/api/EmployeeAPI/" + id

});

return request;

};

});

ShowEmployeeController.js code will be like:

app.controller(‘ShowEmployeeController’, function ($scope, $location, $routeParams, SPACRUDService, ShareData) {

loadEmployee();

function loadEmployee() {

var emp = SPACRUDService.getEmployees();

emp.then(function (pl) {

$scope.Employee = pl.data;

console.log(JSON.stringify($scope.Employee));

},

function (errorPl) {

$scope.error = errorPl;

});

}

});

AddEmployeeController.js code will be like:

app.controller(‘AddEmployeeController’, function ($scope, SPACRUDService) {

$scope.EmployeeId = 0;

$scope.save = function () {

var emp = {

EmployeeId: $scope.EmployeeId,

FirstName: $scope.FirstName,

LastName: $scope.LastName,

Description: $scope.Description,

Salary: $scope.Salary,

Country: $scope.Country,

State: $scope.State,

DateofBirth: $scope.DateofBirth,

IsActive: $scope.IsActive

};

var val = SPACRUDService.post(emp);

val.then(function (pl) {

alert("Employee Saved Successfully.");

},

function (errorPl) {

$scope.error = ‘failure loading Employee’, errorPl;

});

};

});

EditEmployeeController.js code will be like:

app.controller("EditEmployeeController", function ($scope, $location, $routeParams,ShareData, SPACRUDService) {

getEmployee();

function getEmployee() {

ShareData.value = $routeParams.id;

var emp = SPACRUDService.getEmployee(ShareData.value);

emp.then(function (pl) {

$scope.Employee = pl.data;

debugger;

},

function (errorPl) {

$scope.error = ‘failure loading Employee’, errorPl;

});

}

$scope.save = function () {

var emp = {

EmployeeId: $scope.Employee.employeeId,

FirstName: $scope.Employee.firstName,

LastName: $scope.Employee.lastName,

Description: $scope.Employee.description,

Salary: $scope.Employee.salary,

Country: $scope.Employee.country,

State: $scope.Employee.state,

DateofBirth: $scope.Employee.dateofBirth,

IsActive: $scope.Employee.isActive

};

var val = SPACRUDService.put($scope.Employee.employeeId, emp);

val.then(function (pl) {

$location.path("/showEmployee");

},

function (errorPl) {

$scope.error = ‘failure loading Employee’, errorPl;

});

};

});

DeleteEmployeeController.js code will be like:

app.controller(“DeleteEmployeeController”, function ($scope, $location, $routeParams, ShareData, SPACRUDService) {

getEmployee();

function getEmployee() {

ShareData.value = $routeParams.id;

var emp = SPACRUDService.getEmployee(ShareData.value);

emp.then(function (pl) {

$scope.Employee = pl.data;

},

function (errorPl) {

$scope.error = ‘failure loading Employee’, errorPl;

});

}

$scope.delete = function () {

var promiseDeleteEmployee = SPACRUDService.delete(ShareData.value);

promiseDeleteEmployee.then(function (pl) {

$location.path(“/showEmployee”);

},

function (errorPl) {

$scope.error = ‘failure loading Employee’, errorPl;

});

};

});

Now rebuild the solution and run. Following is the output of ShowEmployee page :

clip_image021

Edit page output :

clip_image023

Delete page output :

clip_image025

So, we can see here in each url is having # and to ignore # from the url we need to modify our apps config, it now has Angular’s $locationProvider module as a dependency and we need to enable hashPrefix and html5mode functions. That’s it.

Enable the following code in Module.js

$locationProvider.html5Mode(true).hashPrefix(‘!’);

Our URL’s look better, but hit refresh (F5) & it will throw the 404 error

clip_image027

HTML5 mode is working, but only in a very superficial way. A refresh of the page is sending the full URL to the server (as we have removed the #) which doesn’t know what to do. We can fix this by reconfiguring MVC’s RouteCollection properly. We need to be explicit about the route for each of our views, and then add a catch-all which sends all other URL’s to our landing page, to be handled by Angular.

Update the RegisterRoutes method inside App_Start — > RouteConfig.cs like :

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;

namespace SpaWebApiMVC

{

public class RouteConfig

{

public static void RegisterRoutes(RouteCollection routes)

{

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(

name: "ShowEmployee",

url: "Employee/ShowEmployee",

defaults: new { controller = "Employee", action = "ShowEmployee" }

);

routes.MapRoute(

name: "AddEmployee",

url: "Employee/AddEmployee",

defaults: new { controller = "Employee", action = "AddEmployee", id = UrlParameter.Optional }

);

routes.MapRoute(

name: "EditEmployee",

url: "Employee/EditEmployee/{id}",

defaults: new { controller = "Employee", action = "EditEmployee", id = UrlParameter.Optional }

);

routes.MapRoute(

name: "DeleteEmployee",

url: "Employee/DeleteEmployee/{id}",

defaults: new { controller = "Employee", action = "DeleteEmployee", id = UrlParameter.Optional }

);

routes.MapRoute(

name: "Default",

url: "{*url}",

defaults: new { controller = "Employee", action = "Index", id = UrlParameter.Optional }

);

}

}

}

Now change the below line of codes in ShowEmployee.chtml

<td><a ng-href="editEmployee/{{emp.employeeId}}" class="glyphicon glyphicon-edit">Edit</a></td>

<td><a ng-href="deleteEmployee/{{emp.employeeId}}" class="glyphicon glyphicon-trash">Delete</a></td>

And the below lines in Index.chtml

<td style="border: solid 1px ; width:170px; text-align:center;" class="glyphicon glyphicon-user"><a href="showEmployee"> Show All</a></td>

<td style="border: solid 1px ; width:170px; text-align:center;" class="glyphicon glyphicon-plus"><a href="addEmployee"> Add New</a></td>

Debug the site again, browse around, and test the back/refresh buttons, everything should be working properly now.

Hope, this will help you. If you have any question feel free to write me a note in comments, I will try to explain.

Happy Coding

Tarun Kumar Chatterjee

Category : .Net, Ajax, Angular

Author Info

Tarun Kumar Chatterjee
 
Net – Technology Specialist
 
Rate this article
 
Tarun has been working in IT Industry for over 12+ years. He holds a B-tech degree. He is passionate about learning and sharing the tricks and tips in Azure, .Net ...read more
 

How to restrict direct calls to WebAPI – SharePoint 2013

Sathish Nadarajan
 
Solution Architect
October 19, 2015
 
Rate this article
 
Views
7136

In the previous articles, (WebAPI and Create WEBAPI) we saw how to create and call the WebAPI from our Javascripts in SharePoint 2013. Now, there is one more check, we need to do. i.e., while calling the WebAPI, from the Javascript, we would have given the WebAPI URL on the client Side. Hence, if some one who is having a knowledge of viewing the source, the end user will come to know the WebAPI. Obviously that is not a good practice. We expect the Users to access the WebAPI through our application. Not directly. So a validation needs to be done on the WebAPI.

 [HttpGet]
         public string MyMethod(string sampleParameter)
         {
             Uri urlReferrer = ((System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"]).Request.UrlReferrer;
             if (urlReferrer != null)
             {
                 if (urlReferrer.AbsolutePath.ToLower().Contains("/sites/MySiteCollection") && urlReferrer. Host.ToLower().Contains(“MyDomain”))
                 {
 
                     // Do the actual stuff 
                      
                     return returnValue;
                 }
             }
             return "Not a Valid Request";
         }
 

Hence, on the above code, if the WebAPI is being called directly, the urlReferrer will not have our site collection url. Hence, the Method will return a “Not a valid Request” message.

When the request comes from our site collection, then only the web api will respond.

Though this looks very small, it is really important when considering the security.

Happy Coding,

Sathish Nadarajan.

Category : SharePoint, WebAPI

Author Info

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 15+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business ...read more
 

XMLHttpRequest cannot load http://Site/api/. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://MyCallingSite’ is therefore not allowed access. SharePoint 2013 Error

Sathish Nadarajan
 
Solution Architect
July 6, 2015
 
Rate this article
 
Views
18932

In the previous articles, Creating WebApi, and Calling WebApi, we saw how to create a WebAPI and how to call them from the Javascript.

But, not all the browsers accepts this type of calling. For me, it works like charm in Internet Explorer. But in Chrome, this method does not work. I faced with an exception like No ‘Access-Control-Allow-Origin’. This is something the browser restricts to call the API from different domain.

The purpose of creating an API itself comes under a question at this point. Here, either we can make the API to adopt the CORS rules. There are quite a lot of articles around this.

But in my scenario, instead of passing the JSON Object, we can pass back the JSONP and this is much simpler too.

To pass the JSONP object back, the below steps can be followed.

1. In the Visual Studio Open the Package Manager Console.

clip_image002

2. The console will be opened. On that type

Install-Package WebApiContrib.Formatting.Jsonp

3. We will get the success message as below.

clip_image004

4. Add a Class under “App_Start” as FormatterConfig.cs

 using Newtonsoft.Json;
 using Newtonsoft.Json.Converters;
 using Newtonsoft.Json.Serialization;
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Net.Http.Formatting;
 using System.Net.Http.Headers;
 using System.Threading.Tasks;
 using System.Web;
 using WebApiContrib.Formatting.Jsonp;
 
 namespace MyFirstAPI
 {
     public class JsonpFormatter : JsonMediaTypeFormatter
     {
 
         public JsonpFormatter()
         {
             SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
             SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
 
             JsonpParameterName = "callback";
         }
 
         /// <summary>
         ///  Name of the query string parameter to look for
         ///  the jsonp function name
         /// </summary>
         public string JsonpParameterName { get; set; }
 
         /// <summary>
         /// Captured name of the Jsonp function that the JSON call
         /// is wrapped in. Set in GetPerRequestFormatter Instance
         /// </summary>
         private string JsonpCallbackFunction;
 
 
         public override bool CanWriteType(Type type)
         {
             return true;
         }
 
         /// <summary>
         /// Override this method to capture the Request object
         /// </summary>
         /// <param name="type"></param>
         /// <param name="request"></param>
         /// <param name="mediaType"></param>
         /// <returns></returns>
         public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
         {
             var formatter = new JsonpFormatter()
             {
                 JsonpCallbackFunction = GetJsonCallbackFunction(request)
             };
 
             // this doesn't work unfortunately
             //formatter.SerializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
 
             // You have to reapply any JSON.NET default serializer Customizations here    
             formatter.SerializerSettings.Converters.Add(new StringEnumConverter());
             formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
 
             return formatter;
         }
 
 
         public override Task WriteToStreamAsync(Type type, object value,
                                         Stream stream,
                                         HttpContent content,
                                         TransportContext transportContext)
         {
             if (string.IsNullOrEmpty(JsonpCallbackFunction))
                 return base.WriteToStreamAsync(type, value, stream, content, transportContext);
 
             StreamWriter writer = null;
 
             // write the pre-amble
             try
             {
                 writer = new StreamWriter(stream);
                 writer.Write(JsonpCallbackFunction + "(");
                 writer.Flush();
             }
             catch (Exception ex)
             {
                 try
                 {
                     if (writer != null)
                         writer.Dispose();
                 }
                 catch { }
 
                 var tcs = new TaskCompletionSource<object>();
                 tcs.SetException(ex);
                 return tcs.Task;
             }
 
             return base.WriteToStreamAsync(type, value, stream, content, transportContext)
                        .ContinueWith(innerTask =>
                        {
                            if (innerTask.Status == TaskStatus.RanToCompletion)
                            {
                                writer.Write(")");
                                writer.Flush();
                            }
 
                        }, TaskContinuationOptions.ExecuteSynchronously)
                         .ContinueWith(innerTask =>
                         {
                             writer.Dispose();
                             return innerTask;
 
                         }, TaskContinuationOptions.ExecuteSynchronously)
                         .Unwrap();
         }
 
         /// <summary>
         /// Retrieves the Jsonp Callback function
         /// from the query string
         /// </summary>
         /// <returns></returns>
         private string GetJsonCallbackFunction(HttpRequestMessage request)
         {
             if (request.Method != HttpMethod.Get)
                 return null;
 
             var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
             var queryVal = query[this.JsonpParameterName];
 
             if (string.IsNullOrEmpty(queryVal))
                 return null;
 
             return queryVal;
         }
     }
 }
 

5. The class, I have included in the Source code at the bottom of this post.

6. On the Global.asax.cs, under Application_start method, add this line.

 GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter(new JsonMediaTypeFormatter()));

7. The Demo Controller class will be something like below.

 [HttpGet]
         public string MyAction(string testParameter)
         {
 
 
             //Do the Action Here
             return "My Test";
 
         }
 

8. Now, execute this by clicking the F5.

9. From the Chrome, execute the below line of code.

 try{
 			 $.getJSON("http://localhost:38447/api/demo/MyAction/test?callback=?",
              function (Data) {
               alert(Data);
              })
              .fail(
              function (jqXHR, textStatus, err) {
              alert('Fail ' + err);
              });
 			 }catch(err)
 			 {
 			 
 			 }
 

10. That’s it. Now, we will get the “My Test” string as a JSONP object. Now, we are good to process this.

11. This is a very simple way of communicating with our WebAPI from our SharePoint Client Object Code.

Happy Coding,

Sathish Nadarajan.

Category : JavaScript, SharePoint

Author Info

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 15+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business ...read more
 

How to call WebApi in SharePoint 2013 using Javascript

Sathish Nadarajan
 
Solution Architect
July 2, 2015
 
Rate this article
 
Views
15684

In the previous article, we saw how to create a WebAPI. Now, let us see a small piece of code, how to call that API from your SharePoint 2013 site using Javascript.

I assume that, we have created the API successfully and the API is hosted properly. Now, on our Javascript, a simple call like below will do the rest of the things.

 try{
 			 $.getJSON("http://localhost:47270/api/test/MyAction/Test?callback=?",
              function (Data) {
               alert(Data);
              })
              .fail(
              function (jqXHR, textStatus, err) {
              alert('Fail ' + err);
              });
 			 }catch(err)
 			 {
 			 
 			 }
 

The attribute “?callback?” is added at the last, to make the JSONP return. If we expect only the JSON object, then there is no need of the “?callback?” attribute.

 

Happy Coding,

Sathish Nadarajan.

Author Info

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 15+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business ...read more
 

How to Create a Basic WebApi which can be used by SharePoint 2013

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Views
24849

In this article, let us see how to create a Basic WebApi which can be used by SharePoint 2013. Now, in most of the development environment, the customers were not happy to deploy the WSPs, either it can be a Farm Solution as well as SandBox Solution. Then the solution would be going to Client Object Model and using the Javascript, achieve our functionality.

But in that case also, writing the more complex logics in the JavaScript will also lead to some threats. End user can view the source or inject some other malicious code. To avoid that, we can create some WebApi’s which will be hosted on some other Server. The javascripts will make a call to the API.

The WebAPI which I am referring here is something different from our WCF Service. Let us see how to create a Basic WebAPI step by step.

1. Open the Visual Studio 2013.

clip_image002

2. On the new Project, select the template as shown above.

3. On the Template screen, select the options as shown below.

clip_image003

4. The solution will be as shown below.

clip_image005

5. Add a Controller Class

clip_image007

6. Select the Web API 2 Controller Empty.

clip_image009

7. Give the Name as DemoController.

8. Create a Sample Method as shown below.

 [HttpGet]
         public string MyAction(string testParameter)
         {
 
 
             //Do the Action Here
             return “My Test”;
 
         }
 

10. By default the WebApiConfig file will have the below code.

 public static void Register(HttpConfiguration config)
         {
             // Web API configuration and services
 
             // Web API routes
             config.MapHttpAttributeRoutes();
 
             config.Routes.MapHttpRoute(
                 name: "DefaultApi",
                 routeTemplate: "api/{controller}/{id}",
                 defaults: new { id = RouteParameter.Optional }
             );
 
              
         }
 

11. Along with that add the below Routes as well. Hence the code will looks like

 public static void Register(HttpConfiguration config)
         {
             // Web API configuration and services
 
             // Web API routes
             config.MapHttpAttributeRoutes();
 
             config.Routes.MapHttpRoute(
                 name: "DefaultApi",
                 routeTemplate: "api/{controller}/{id}",
                 defaults: new { id = RouteParameter.Optional }
             );
 
             config.Routes.MapHttpRoute(
                 name: "SearchApi",
                 routeTemplate: "api/{controller}/{action}/{testParameter}",
                 defaults: new { action = "Get" }
             );
         }
 
 

12. Now, our API is ready to execute. Click F5 from the Visual Studio. It will be loaded on the local virtual IIS.

13. Now, on the browser, type the URL like, http://<<localhost>>/api/demo/MyAction/test

14. Our Controller method will be called and we will get back a String as JSON Object.

15. Even, if we want to return as a Parsed JSON Object, we can use the below code.

 [HttpGet]
         public IHttpActionResult MyAction (string docTopic,string termID)
         {
              
                     return Json(“My Test”);
                     
  
         }
 

Download the Sample Config Files Here

Happy Coding,

Sathish Nadarajan.

Category : SharePoint, WebAPI

Author Info

Sathish Nadarajan
 
Solution Architect
 
Rate this article
 
Sathish is a Microsoft MVP for SharePoint (Office Servers and Services) having 15+ years of experience in Microsoft Technologies. He holds a Masters Degree in Computer Aided Design and Business ...read more
 

Leave a comment