Skip to content

DnnFable/Dnn.Fable.Connector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dnn.Fable.Connector

Introduction

DNN as Web Application Framework offers multiple ways of writing extensions. Modules can be written as WebForms, MVC or simple SPA modules.

SPA modules are typically build with HTML 5, CSS and JS-Frameworks like React, Angular or Vue.js. These frameworks talk to Web API based services for querying data or executing scripts.

Functional Programming is growing popularity and tools like Elm lang are also available in DNN. In fact, it is possible to write both client side code and the services in F#. Combining the excellent F# to Javascript compiler Fable, the powerfull Elmish Model-View-Update implementation and the Web API written in F# directly provides a modern type safe webstack.

And all plays well with DNN, and developers can benefit from the proven security and extensibility.

Dnn.Fable.Fetch and Dnn.Fable.ApiController simplify the communication between Web Api and the SPA module.

Dnn.Fable.ApiController Nuget

Usually DNN Web Services inherit from DotNetNuke.Web.Api.DnnApiController, which is again based on Web Api. It is a perfect match, it is only that it uses Newtonsoft.Json for encoding and decoding data for requests and responses.

This is fine for C# structs and classes, but just ugly and error prone for F# data structures like discriminated unions or option types. The fable project Thoth.Json(.Net) offers several packages which simplifies and prettifies encoding and decoding. This project is a wrapper of these packages and targets DNN.

It is now possible (nbut not nescessary) to share code between service and client.

shared.fs:

module SharedData

type User =
    | Unauthenticated
    | LoggedInAs of
        {| DisplayName: string
           UserId: int |}

The web service in F# is quite familiar beside from the syntax, the only difference is that it inherits now from Dnn.Fable.ApiController

namespace MyCompany.MyModule

open SharedData
open System.Web.Http
open System.Net
open System.Net.Http
open DotNetNuke.Web.Api
open DotNetNuke.Security

type RouteMapper ()=
    interface IServiceRouteMapper with
      member __.RegisterRoutes (rtm:IMapRoute) = 
        let namespaces = [|"MyCompany.MyModule"|]
        let moduleApiName ="mycomp/mymod" 
        rtm.MapHttpRoute  (moduleApiName, "default", "{controller}/{action}", namespaces) |> ignore                                

type MyServiceController() = 
    inherit Dnn.Fable.ApiController() // instead of DnnApiController

    [<HttpGet>]
    [<ValidateAntiForgeryToken>] 
    [<DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)>] 
    member __.CurrentUser () =
        let result = 
            if __.User.Identity.IsAuthenticated 
                then LoggedInAs 
                      {| DisplayName =__.UserInfo.DisplayName; 
                         UserId =__.UserInfo.UserID |}
                else Unauthenticated

        __.Request.CreateResponse (HttpStatusCode.OK, result)

Dnn.Fable.Fetch Nuget

DNN provides a ServicesFramework as JQuery extension which is using AJAX calls to talk to the service. A more modern approach would be based on fetch. However, each call needs the correct ModuleHeaders with moduleId, tabId, antiForgeryToken and maybe also requestCredentials.

The moduleId needs to be passed as Token within the SPA template:

_index.html:

[AntiForgeryToken: {}]
<div id="myModule-container" data-moduleId ="[ModuleContext:ModuleId]"/>
[JavaScript:{ path: "~/desktopModules/mycomp/mymod/bundle.js", provider:"DnnFormBottomProvider"}]

Then this script talks to the service above and queries the current user.

module.fs:

namespace HelloAgain

open SharedData
open Browser

let moduleName = "mycomp/mymod" // same as in RouterMapper
let url = "myservice/currentuser" // MyServiceController.CurrentUser()

let container = document.getElementById "myModule-container"
let moduleId = container.dataset.["moduleId"]

Dnn.Fable.Fetch.get(moduleId, moduleName, url) // returns a Promise<User>
|> Promise.iter ( fun user -> 
    let text =
        match user with
        | Unauthenticated -> "Please login"
        | LoggedInAs p    -> sprintf "Hello %s" p.DisplayName
    container.innerText <- text ) 

Dnn.Fable.Fetch offers methods to handle GET, POST, PUT, DELETE, PATCH requests.

Credits

Dnn.Fable Fetch and ApiController are build on top of Thoth.Json.Net and Thoth.Fetch

About

Fetch and ApiController for simple usage of DNN and Fable

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages