ASP.NET Core app with WebSharper

To use WebSharper features in an ASP.NET Core web application, you will need the packages WebSharper, WebSharper.FSharp and WebSharper.AspNetCore. The easiest way to get started is to use one of the templates.

WebSharper can be enabled and configured in the Configure method, and uses a few services configured in the ConfigureService method. Here is a recommended default setup:

type Startup() =

    member this.ConfigureServices(services: IServiceCollection) =
        services.AddSitelet(mySitelet)
                .AddAuthentication("WebSharper")
                .AddCookie("WebSharper")
        |> ignore
        
    member this.Configure(app: IApplicationBuilder) =
        app.UseAuthentication()
            .UseWebSharper()
            .UseStaticFiles()
        |> ignore

Configure setup

UseWebSharper()

By having open WebSharper.AspNetCore, the UseWebSharper extension method is available for IApplicationBuilder. It installs the middlewares for WebSharper runtime.

For a basic setup, simply call UseWebSharper():

type Startup() =

    member this.Configure(app: IApplicationBuilder) =
        app.UseWebSharper()
        |> ignore

You can also configure WebSharper by passing a builder function. For example:

type Startup() =

    member this.Configure(app: IApplicationBuilder) =
        app.UseWebSharper(fun builder ->
            builder
                .Sitelet(mySitelet)
                .UseRemoting(false)
            |> ignore)
        |> ignore

The following methods are available for the builder:

  • builder.UseSitelets(?u: bool) tells WebSharper whether to serve sitelets; the default is true.

    If this is true, then either builder.Sitelet() or services.AddSitelet() must be called to declare the sitelet to serve; otherwise, a runtime error is thrown.

  • builder.UseRemoting(?u: bool) tells WebSharper whether to serve remote functions; the default is true.

  • builder.Sitelet(s: Sitelet<'EndPoint>) tells WebSharper to serve the given sitelet. This will be ignored if UseSitelets(false) is called. Note that it is advised to pass the sitelet as a service rather than through the builder; see Sitelets for more details.

  • builder.Config(c: IConfiguration) tells WebSharper to use this appSettings configuration. By default, WebSharper uses the websharper subsection of the host configuration.

  • builder.Logger(l: ILogger) tells WebSharper to use this logger for its internal messages. By default, WebSharper uses an injected ILogger<WebSharperOptions>.

  • builder.BinDir(d: string) tells WebSharper to look for assemblies with WebSharper metadata in this directory. By default, it uses the directory where WebSharper.AspNetCore.dll is located.

  • builder.AuthenticationScheme(s: string) tells WebSharper to use the given authentication scheme for Web.Context.UserSession. See Authentication for more details.

UseStaticFiles()

If you have any client-side code, then it will need access to compiled JavaScript files, which the WebSharper middleware does not provide. So you need to add the default static files provider:

app.UseWebSharper()
    .UseStaticFiles()

Note that UseStaticFiles() should be called after UseWebSharper(). This way, WebSharper sitelets and remoting have a chance to handle requests even if they also happen to match a static file.

UseAuthentication()

If you wish to use Web.Context.UserSession, then you need to call app.UseAuthentication(), and to configure its services.

app.UseAuthentication()
    .UseWebSharper()
    .UseStaticFiles()

Note that UseAuthentication() should be called before UseWebSharper(). This way, Authentication can parse session information from the request that WebSharper can then use.

ConfigureServices setup

WebSharper uses ASP.NET Core's dependency injection for a number of services.

Sitelets

The recommended way to tell WebSharper which sitelet to serve is to use services.AddSitelet(). There are several variants:

  • AddSitelet(s: Sitelet<'EndPoint>) simply uses the given Sitelet value.

    type Startup() =
    
        member this.ConfigureServices(services: IServiceCollection) =
            services.AddSitelet(mySitelet)
    
  • AddSitelet<'T when 'T :> ISiteletService>() uses a Sitelet created through dependency injection. In F#, the best way to create a Sitelet service is by implementing the abstract class SiteletService<'EndPoint>.

    type MyWebsite() =
        inherit SiteletService<MyEndPoint>()
    
        let sitelet = Application.MultiPage(fun ctx endpoint ->
            Content.Text "Hello, world!"
        )
    
        override this.Sitelet = sitelet
    
    type Startup() =
    
        member this.ConfigureServices(services: IServiceCollection) =
            services.AddSitelet<MyWebsite>()
    

    The type MyWebsite can now use injected services:

    type MyWebsite(logger: ILogger<MyWebsite>) =
        inherit SiteletService<MyEndPoint>()
    
        let sitelet = Application.MultiPage(fun ctx endpoint ->
            logger.LogInformation("Serving a sitelet page!")
            Content.Text "Hello, world!"
        )
    
        override this.Sitelet = sitelet
    

Remoting

To use simple module-based remoting, there is nothing to add: as long as there is no UseRemoting(false) in your call to UseWebSharper(), remote module functions will be served.

WebSharper.AspNetCore additionally provides a way to inject a remoting handler (see Server-side customization here) with dependencies. For this, simply register your handler using services.AddWebSharperRemoting<_>(). This call replaces the call to WebSharper.Core.Remoting.AddHandler that would otherwise be needed.

// Rpc handler:
type MyRpc(logger: ILogger<MyRpc>) =

    [<Remote>]
    member this.MyMethod() =
        logger.LogInformation("MyMethod was called")

// Registering:
type Startup() =

    member this.ConfigureServices(services: IServiceCollection) =
        services.AddWebSharperRemoting<MyRpc>()
        |> ignore

If you have an abstract remoting handler, you can register an implementation using AddWebSharperRemoting<_, _>().

// Abstract Rpc handler:
[<AbstractClass>]
type MyRpc() =
    [<Remote>]
    abstract MyMethod : unit -> unit

// Implementation of the Rpc handler:
type MyRpcImpl(logger: ILogger<MyRpc>) =
    inherit MyRpc()

    override this.MyMethod() =
        logger.LogInformation("MyMethod was called")

// Registering:
type Startup() =

    member this.ConfigureServices(services: IServiceCollection) =
        services.AddWebSharperRemoting<MyRpc, MyRpcImpl>()
        |> ignore

Authentication

To use WebSharper's user sessions, add this to your ConfigureServices method:

type Startup() =

    member this.ConfigureServices(services: IServiceCollection) =
        services
            .AddAuthentication("WebSharper")
            .AddCookie("WebSharper")
        |> ignore

and also app.UseAuthentication() to Configure.

If you configured a different authentication scheme in UseWebSharper(), then you need to use that same scheme name here instead of "WebSharper".