More and more .NET developers come to appreciate functional programming, and this is really good news for F#. A couple weeks ago Scott Hanselman ran a short article on running Suave.io apps as Azure web apps deployed via GitHub. His setup was the following:
site\webserver.fsx
) inside an empty ASP.NET project with a web.config
.deploy.cmd
via .deployment
) found in your project.build.fsx
) by passing it to fsi.exe
shipped with FAKE.<httpPlatform>
directive. This in turn uses FAKE to execute site\webserver.fsx
, which starts Suave.io and listens on a given port that is then mapped to the standard HTTP port.This sequence is more complicated than it needs to be because it has to work around the lack of the F# tools and core libraries when setting up a new Azure web app deployment. Since Scott's article, I filed a ticket to bundle the F# compiler and tools in a Nuget, and thanks to Don Syme and Steffen Forkmann, that Nuget was out within a couple days.
Armed with that, we quickly put together a similar deployment setup for WebSharper client-server apps (other types of WebSharper apps are equally possible.) This has the obvious advantage that:
Loic blogged the technical details in his WebSharper: From Zero to an Azure-deployed Web Application article, using a WebSharper implementation/clone of the "2048" game. I also put together a simpler template that you can use for client-server applications, and it's available in...
The template uses Paket instead of Nuget, and you can build your project with build.cmd
(you will need curl installed and on your path). Run build.cmd
before opening in Visual Studio as well.
You can also get started quickly by clicking here:
There is a single main.fs
file, and it contains both the server and the client functionality:
namespace MyApplication
open WebSharper
open WebSharper.Sitelets
type Action =
| [<CompiledName "">] Home
| [<CompiledName "about">] About
module Server =
[<Rpc>]
let DoSomething input =
let R (s: string) = System.String(List.ofSeq s |> List.rev |> Array.ofList)
async {
return R input
}
[<JavaScript>]
module Client =
open WebSharper.Html.Client
let Main () =
let input = Input [Attr.Value ""]
let output = H1 []
Div [
input
Button [Text "Send"]
|>! OnClick (fun _ _ ->
async {
let! data = Server.DoSomething input.Value
output.Text <- data
}
|> Async.Start
)
HR []
H4 [Class "text-muted"] -- Text "The server responded:"
Div [Class "jumbotron"] -< [output]
]
open WebSharper.Html.Server
module Skin =
open System.Web
type Page =
{
Title : string
Menubar : Element list
Body : Element list
}
let MainTemplate =
Content.Template<Page>("~/Main.html")
.With("title", fun x -> x.Title)
.With("menubar", fun x -> x.Menubar)
.With("body", fun x -> x.Body)
let Menubar (ctx: Context<Action>) action =
let ( => ) text act =
LI [if action = act then yield Class "active"] -< [
A [HRef (ctx.Link act)] -< [Text text]
]
[
LI ["Home" => Action.Home]
LI ["About" => Action.About]
]
let WithTemplate action title body : Content<Action> =
Content.WithTemplate MainTemplate <| fun ctx ->
{
Title = title
Menubar = Menubar ctx action
Body = body ctx
}
module Site =
module Pages =
let Home =
Skin.WithTemplate Action.Home "Home" <| fun ctx ->
[
H1 [Text "Say Hi to Azure"]
Div [ClientSide <@ Client.Main() @>]
]
let About =
Skin.WithTemplate Action.About "About" <| fun ctx ->
[
H1 [Text "About"]
P [Text "This is a template WebSharper client-server application
that you can easily deploy to Azure from source control."]
]
let Main =
Sitelet.Infer (function
| Action.Home -> Pages.Home
| Action.About -> Pages.About
)
[<Sealed>]
type Website() =
interface IWebsite<Action> with
member this.Sitelet = Site.Main
member this.Actions = [Action.Home; Action.About]
[<assembly: Website(typeof<Website>)>]
do ()
This uses basic WebSharper templating and a master main.html
template and implements a two page sitelet application:
You can run it directly here.
Happy coding!
Can’t find what you were looking for? Drop us a line.
20221229 · 30 min read
20211230 · 20 min read