Skip to main content
Home  › ... Razor

WebAPI and REST Tutorials

Tutorial HomeWebApi and REST

App.Data WebApi Examples

In this example, we'll get app-data from the WebApi.

Important: WebApi using [JsonFormatter] 🆕 in 2sxc 16
The [JsonFormatter] has a few effects. It auto-defaults to camelCase (but you can modify this if you need to preserve casing). It will also auto-convert entities to a standard JSON format.

In this example, we'll get app-data from the WebApi.

Click to see the result of a WebApi call with the shared code:

Result

@inherits Custom.Hybrid.Razor14

<button type="button" class="btn btn-primary" onclick="getPersons(this)">
  Get Persons Custom
</button> 
<button type="button" class="btn btn-primary" onclick="getPersonsAuto(this)">
  Persons with Auto-Convert
</button> 
<button type="button" class="btn btn-primary" onclick="getPersonsAutoPreserveCasing(this)">
  Persons with Auto-Convert, preserve casing
</button> 
  
<script>
  function getPersons(moduleContext) {
    $2sxc(moduleContext).webApi.fetchJson('books/persons')
      .then(function(data) {
        alert('Found ' + data.length + ' persons. \n'
          + 'The first one is "' + data[0].FirstName + ' ' + data[0].LastName + '"\n\n'
          + 'The raw JSON: \n' + JSON.stringify(data)
        );
      });
  }
  function getPersonsAuto(moduleContext) {
    $2sxc(moduleContext).webApi.fetchJson('books/personsAuto')
      .then(function(results) {
        alert('On WebApi with Auto-Convert: Found ' + results.length + ' persons. \n'
          + 'The first one is "' + results[0].firstName + ' ' + results[0].lastName + '"\n\n'
          + 'The raw JSON: \n' + JSON.stringify(results)
        );
      });
  }

  function getPersonsAutoPreserveCasing(moduleContext) {
    $2sxc(moduleContext).webApi.fetchJson('books/PersonsAutoPreserveCasing')
      .then(function(results) {
        alert('On WebApi with Auto-Convert: Found ' + results.length + ' persons. \n'
          + 'The first one is "' + results[0].FirstName + ' ' + results[0].LastName + '"\n\n'
          + 'The raw JSON: \n' + JSON.stringify(results)
        );
      });
  }
</script>

Source Code of BooksController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
#endif
using System.Linq;        // this enables .Select(x => ...)
using ToSic.Sxc.WebApi;   // For the [JsonFormatter] (see below)
using ToSic.Eav.DataFormats.EavLight; // For Auto-Conversion (see below)

[AllowAnonymous]                          // all commands can be accessed without a login
[ValidateAntiForgeryToken]                // protects API from users not on your site (CSRF protection)
public class BooksController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public object Persons()
  {
    // we could do: return App.Data["Persons"];
    // but the raw data is difficult to use, so we'll improve it

    // instead we'll create nice objects for our use
    return AsList(App.Data["Persons"])
      .Select(p => new {
        Id = p.EntityId,
        p.FirstName,
        p.LastName,
        Picture = p.Mugshot,
      });
  }

  [HttpGet]
  [JsonFormatter]                           // this will auto-convert Entities to JSON
  public object PersonsAuto()
  {
    return App.Data["Persons"];
  }

  [HttpGet]
  [JsonFormatter(Casing = Casing.Preserve)] // auto-convert but preserve casing
  public object PersonsAutoPreserveCasing()
  {
    return App.Data["Persons"];
  }
  

  [HttpGet]
  public object Books()
  {
    return AsList(App.Data["Books"])
      .Select(b => new {
        Id = b.EntityId,
        b.Title,
        b.Cover,
        Awards = AsList(b.Awards as object)
          .Select(a => a.EntityTitle)
      });
  }
}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace

In this example, we'll get app-data from the WebApi.

Click to see the result of a WebApi call with the shared code:

Result

@inherits Custom.Hybrid.Razor14

<button type="button" class="btn btn-primary" onclick="getBooks(this)">
  Get Books Custom
</button>
  
<script>
  function getBooks(moduleContext) {
    $2sxc(moduleContext).webApi.fetchJson('books/books')
      .then(function(results) {
        alert('Found ' + results.length + ' books. \n'
          + 'The first one is "' + results[0].Title + '"\n\n'
          + 'The raw JSON: \n' + JSON.stringify(results)
          );
      });
  }
</script>

Source Code of BooksController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
#endif
using System.Linq;        // this enables .Select(x => ...)
using ToSic.Sxc.WebApi;   // For the [JsonFormatter] (see below)
using ToSic.Eav.DataFormats.EavLight; // For Auto-Conversion (see below)

[AllowAnonymous]                          // all commands can be accessed without a login
[ValidateAntiForgeryToken]                // protects API from users not on your site (CSRF protection)
public class BooksController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public object Persons()
  {
    // we could do: return App.Data["Persons"];
    // but the raw data is difficult to use, so we'll improve it

    // instead we'll create nice objects for our use
    return AsList(App.Data["Persons"])
      .Select(p => new {
        Id = p.EntityId,
        p.FirstName,
        p.LastName,
        Picture = p.Mugshot,
      });
  }

  [HttpGet]
  [JsonFormatter]                           // this will auto-convert Entities to JSON
  public object PersonsAuto()
  {
    return App.Data["Persons"];
  }

  [HttpGet]
  [JsonFormatter(Casing = Casing.Preserve)] // auto-convert but preserve casing
  public object PersonsAutoPreserveCasing()
  {
    return App.Data["Persons"];
  }
  

  [HttpGet]
  public object Books()
  {
    return AsList(App.Data["Books"])
      .Select(b => new {
        Id = b.EntityId,
        b.Title,
        b.Cover,
        Awards = AsList(b.Awards as object)
          .Select(a => a.EntityTitle)
      });
  }
}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace

Result

@inherits Custom.Hybrid.Razor14

<button type="button" class="btn btn-primary" onclick="getPersons(this)">
  Get Persons Custom
</button> 
  
<script>
  function getPersons(moduleContext) {
    $2sxc(moduleContext).webApi.fetchJson('books/persons')
      .then(function(data) {
        alert('Found ' + data.length + ' persons. \n'
          + 'The first one is "' + data[0].FirstName + ' ' + data[0].LastName + '"\n\n'
          + 'The raw JSON: \n' + JSON.stringify(data)
        );
      });
  }
</script>

Source Code of BooksController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
#endif
using System.Linq;        // this enables .Select(x => ...)
using ToSic.Sxc.WebApi;   // For the [JsonFormatter] (see below)
using ToSic.Eav.DataFormats.EavLight; // For Auto-Conversion (see below)

[AllowAnonymous]                          // all commands can be accessed without a login
[ValidateAntiForgeryToken]                // protects API from users not on your site (CSRF protection)
public class BooksController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public object Persons()
  {
    // we could do: return App.Data["Persons"];
    // but the raw data is difficult to use, so we'll improve it

    // instead we'll create nice objects for our use
    return AsList(App.Data["Persons"])
      .Select(p => new {
        Id = p.EntityId,
        p.FirstName,
        p.LastName,
        Picture = p.Mugshot,
      });
  }

  [HttpGet]
  [JsonFormatter]                           // this will auto-convert Entities to JSON
  public object PersonsAuto()
  {
    return App.Data["Persons"];
  }

  [HttpGet]
  [JsonFormatter(Casing = Casing.Preserve)] // auto-convert but preserve casing
  public object PersonsAutoPreserveCasing()
  {
    return App.Data["Persons"];
  }
  

  [HttpGet]
  public object Books()
  {
    return AsList(App.Data["Books"])
      .Select(b => new {
        Id = b.EntityId,
        b.Title,
        b.Cover,
        Awards = AsList(b.Awards as object)
          .Select(a => a.EntityTitle)
      });
  }
}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace

Result

@inherits Custom.Hybrid.Razor14

<button type="button" class="btn btn-primary" onclick="getPersonsAutoPreserveCasing(this)">
  Persons with Auto-Convert, preserve casing
</button> 
  
<script>
  function getPersonsAutoPreserveCasing(moduleContext) {
    $2sxc(moduleContext).webApi.fetchJson('books/PersonsAutoPreserveCasing')
      .then(function(results) {
        alert('On WebApi with Auto-Convert: Found ' + results.length + ' persons. \n'
          + 'The first one is "' + results[0].FirstName + ' ' + results[0].LastName + '"\n\n'
          + 'The raw JSON: \n' + JSON.stringify(results)
        );
      });
  }
</script>

Source Code of BooksController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
#endif
using System.Linq;        // this enables .Select(x => ...)
using ToSic.Sxc.WebApi;   // For the [JsonFormatter] (see below)
using ToSic.Eav.DataFormats.EavLight; // For Auto-Conversion (see below)

[AllowAnonymous]                          // all commands can be accessed without a login
[ValidateAntiForgeryToken]                // protects API from users not on your site (CSRF protection)
public class BooksController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public object Persons()
  {
    // we could do: return App.Data["Persons"];
    // but the raw data is difficult to use, so we'll improve it

    // instead we'll create nice objects for our use
    return AsList(App.Data["Persons"])
      .Select(p => new {
        Id = p.EntityId,
        p.FirstName,
        p.LastName,
        Picture = p.Mugshot,
      });
  }

  [HttpGet]
  [JsonFormatter]                           // this will auto-convert Entities to JSON
  public object PersonsAuto()
  {
    return App.Data["Persons"];
  }

  [HttpGet]
  [JsonFormatter(Casing = Casing.Preserve)] // auto-convert but preserve casing
  public object PersonsAutoPreserveCasing()
  {
    return App.Data["Persons"];
  }
  

  [HttpGet]
  public object Books()
  {
    return AsList(App.Data["Books"])
      .Select(b => new {
        Id = b.EntityId,
        b.Title,
        b.Cover,
        Awards = AsList(b.Awards as object)
          .Select(a => a.EntityTitle)
      });
  }
}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace

In this example, we'll get app-data from the WebApi which runs a query with a parameter.

Click to see the result of a WebApi call with the shared code:

Result

@inherits Custom.Hybrid.Razor14

@{
  var authorId = AsDynamic(App.Data["Persons"].First()).EntityId;
}
<button type="button" class="btn btn-primary" onclick="getAuthor(this, @authorId)">
  Get Author #@authorId
</button> 
  
<script>
  function getAuthor(moduleContext, authorId) {
    $2sxc(moduleContext).webApi.fetchJson('authorsquery/get?authorId=' + authorId)
      .then(function (author) {
        alert('Found author: '
          + author.FirstName + ' ' + author.LastName 
          + ' (with ' + author.Books.length + ' books) \n\n'
          + 'The raw JSON: \n' + JSON.stringify(author)
          );
      });    
    }
</script>

Source Code of AuthorsQueryController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
#endif
using System.Linq;                        // this enables .First() or .Select(x => ...)

// Tutorial note: This is used in the WebApi demos for Querying App Data

[AllowAnonymous]                          // all commands can be accessed without a login
[ValidateAntiForgeryToken]                // protects API from users not on your site (CSRF protection)
public class AuthorsQueryController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public object Get(int authorId)
  {
    var query = Kit.Data.GetQuery("AuthorsWithBooks", parameters: new {
      AuthorId = authorId
    });
    
    var a = AsDynamic(query["Current"].First());

    return new {
        Id = a.EntityId,
        a.FirstName,
        a.LastName,
        Picture = a.Mugshot,
        Books = AsList(query["CurrentBooks"])
          .Select(b => new {
            Id = b.EntityId,
            b.Title
          })
      };
  }

}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace

This is an advanced implementation of the basic examples before. It uses #if statements to ensure that the same code can run in Dnn and Oqtane.

Click to see the result of a WebApi call with the shared code:

Result

@inherits Custom.Hybrid.Razor14

@* Make sure anonymous users have the 2sxc JS API *@
@Kit.Page.Activate("2sxc.JsCore")
<button type="button" class="btn btn-primary" onclick="callBasicHello(this)">
  Get Hello
</button> 
  
<script>
  function callBasicHello(moduleContext) {
    $2sxc(moduleContext).webApi.fetchJson('app/auto/api/hybrid/hello')
      .then(function (results) { alert(results); });
  }
</script>

Source Code of HybridController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
using Oqtane.Shared;                      // Oqtane role names for [Authorize]
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
using DotNetNuke.Security;                // DnnRoles for SecurityAccessLevel.Anonymous
#endif

#if NETCOREAPP
[Produces("application/json")]  // Ensures that strings are JSON and have quotes around them in .net 5
#endif
[AllowAnonymous]                          // all commands can be accessed without a login
public class HybridController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [AllowAnonymous]                        // Explicitly set for this method
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public string Hello()
  {
    return "Hello from the basic controller in /api";
  }

  #if OQTANE
  [Authorize(Roles = RoleNames.Everyone)] // Oqtane authorize everyone / anonymous
  #else
  [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Anonymous)] // Dnn equivalent
  #endif
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public int Square(int number)
  {
    return number * number;
  }
}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace

This is an advanced implementation of the basic examples before. It uses #if statements to ensure that the same code can run in Dnn and Oqtane.

Click to see the result of a WebApi call with the shared code:

Result

@inherits Custom.Hybrid.Razor14

@* Make sure anonymous users have the 2sxc JS API *@
@Kit.Page.Activate("2sxc.JsCore")
<button type="button" class="btn btn-primary" onclick="callSquare(this, 7)">
  Square 7
</button> <button type="button" class="btn btn-primary" onclick="callSquare(this, 27)">
  Square 27
</button>
  
<script>
  function callSquare(moduleContext, original) {
    $2sxc(moduleContext).webApi.fetchJson('app/auto/api/hybrid/square?number=' + original)
      .then(function (results) { alert(results); });
  }
</script>

Source Code of HybridController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
using Oqtane.Shared;                      // Oqtane role names for [Authorize]
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
using DotNetNuke.Security;                // DnnRoles for SecurityAccessLevel.Anonymous
#endif

#if NETCOREAPP
[Produces("application/json")]  // Ensures that strings are JSON and have quotes around them in .net 5
#endif
[AllowAnonymous]                          // all commands can be accessed without a login
public class HybridController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [AllowAnonymous]                        // Explicitly set for this method
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public string Hello()
  {
    return "Hello from the basic controller in /api";
  }

  #if OQTANE
  [Authorize(Roles = RoleNames.Everyone)] // Oqtane authorize everyone / anonymous
  #else
  [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Anonymous)] // Dnn equivalent
  #endif
  [HttpGet]                               // [HttpGet] says we're listening to GET requests
  public int Square(int number)
  {
    return number * number;
  }
}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace