Three easy ways to create simple Web Server with F#

I have tried to find easiest ways to create a simple web server with F#. There are three most simple ways to do it.

The goal is to create a simple web service that maps web request urls to the files in the site folder. If file with such name exists then return its content as html. Assume that all html files located in ‘D:\mySite\‘.

HttpListener

First and probably the most promising option was created by Julian Kay and described in his post “Creating a simple HTTP Server with F#“. I slightly modified source code to satisfy my initial goal. You can find detailed description of how it works in Julian’s post. (Works from FSI)

open System
open System.Net
open System.Text
open System.IO

let siteRoot = @"D:\mySite\"
let host = "http://localhost:8080/"

let listener (handler:(HttpListenerRequest->HttpListenerResponse->Async<unit>)) =
    let hl = new HttpListener()
    hl.Prefixes.Add host
    hl.Start()
    let task = Async.FromBeginEnd(hl.BeginGetContext, hl.EndGetContext)
    async {
        while true do
            let! context = task
            Async.Start(handler context.Request context.Response)
    } |> Async.Start

let output (req:HttpListenerRequest) =
    let file = Path.Combine(siteRoot,
                            Uri(host).MakeRelativeUri(req.Url).OriginalString)
    printfn "Requested : '%s'" file
    if (File.Exists file)
        then File.ReadAllText(file)
        else "File does not exist!"

listener (fun req resp ->
    async {
        let txt = Encoding.ASCII.GetBytes(output req)
        resp.ContentType <- "text/html"
        resp.OutputStream.Write(txt, 0, txt.Length)
        resp.OutputStream.Close()
    })
// TODO: add your code here

Self-hosted WCF service

The second option is a tuned self-hosted WCF service. This approach was proposed by  Brian McNamara as an answer to the StackOverflow question “F# web server library“. (Works from FSI)

#r "System.ServiceModel.dll"
#r "System.ServiceModel.Web.dll"

open System
open System.IO

open System.ServiceModel
open System.ServiceModel.Web

let siteRoot = @"D:\mySite\"

[<ServiceContract>]
type MyContract() =
    [<OperationContract>]
    [<WebGet(UriTemplate="{file}")>]
    member this.Get(file:string) : Stream =
        printfn "Requested : '%s'" file
        WebOperationContext.Current.OutgoingResponse.ContentType <- "text/html"
        let bytes = File.ReadAllBytes(Path.Combine(siteRoot, file))
        upcast new MemoryStream(bytes)

let startAt address =
    let host = new WebServiceHost(typeof<MyContract>, new Uri(address))
    host.AddServiceEndpoint(typeof<MyContract>, new WebHttpBinding(), "")
      |> ignore
    host.Open()
    host

let server = startAt "http://localhost:8080/"
// TODO: add your code here
server.Close()

NancyFx

The third one is based on NancyFx. It is lightweight, low-ceremony, framework for building HTTP based services on .Net and Mono. Nancy is a popular framework in C# world, but does not have a natural support of F#. The F# code looks not so easy and simple as it could be. If you want to make it work, you need to create console application and install the Nancy and Nancy.Hosting.Self NuGet packages.

module WebServers

open System
open System.IO
open Nancy
open Nancy.Hosting.Self
open Nancy.Conventions

let (?) (this : obj) (prop : string) : obj =
    (this :?> DynamicDictionary).[prop]

let siteRoot = @"d:\mySite\"

type WebServerModule() as this =
    inherit NancyModule()
    do this.Get.["{file}"] <-
         fun parameters ->
              new Nancy.Responses.HtmlResponse(
                  HttpStatusCode.OK,
                  (fun (s:Stream) ->
                      let file = (parameters?file).ToString()
                      printfn "Requested : '%s'" file
                      let bytes = File.ReadAllBytes(Path.Combine(siteRoot, file))
                      s.Write(bytes,0,bytes.Length)
              )) |> box

let startAt host =
    let nancyHost = new NancyHost(new Uri(host))
    nancyHost.Start()
    nancyHost

let server = startAt "http://localhost:8080/"
printfn "Press [Enter] to exit."
Console.ReadKey() |> ignore
server.Stop()

Further reading

Live tweets from Alea.cuBase F#-for-financial-GPU event in London.

Yesterday, there was Alea.cuBase live coding session. Don Syme led live tweets from inside. This post is dedicated to those who missed it.

Read more of this post

F# Weekly #19, 2013

Welcome to F# Weekly,

Tiny weekly this time, a short roundup of F# content from this past week:

News

Videos/Presentations

Blogs

P.S. The top 20 data visualisation tools

That’s all for now.  Have a great week.

Previous F# Weekly edition – #18

Json.Net vs ServiceStack.Text

Reblogged from Daniel Wertheim:

Click to visit the original post
  • Click to visit the original post

This is going to be a verry short comparision of the two Json-serialization frameworks:
Json.Net - http://json.codeplex.com
ServiceStack.Text - https://github.com/mythz/ServiceStack.Text

I have always used Json.Net when dealing with serialization of entities back and forth to Json, but then I stumbled upon this: "Fastest JSON Serializer for .NET released" - http://www.servicestack.net/mythz_blog/?p=344 Man was I excited, since I need the "best" performance I can get for this in SisoDb (

Read more… 563 more words

I can not understand why JSON.NET so popular (http://www.servicestack.net/mythz_blog/?p=344) Or maybe I am wrong and picture have changed ? Need to test it!

F# Weekly #18, 2013

FsMLWelcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos/Presentations

Blogs

That’s all for now.  Have a great week.

Previous F# Weekly edition – #17TsunamiBeta

F# Weekly #17, 2013

MSR_ML_2013Welcome to F# Weekly,

A roundup of F# content from this past week:

News

Videos

Blogs

That’s all for now.  Have a great week.

Previous F# Weekly edition – #16

Mike Falanga speaks about Discriminated Unions at Cleveland F# SIG

Reblogged from do stuff, write about it, simple:

I was able to record the Cleveland F# SIG, where Mike Falanga spoke about F# language's Discriminated Unions feature (http://msdn.microsoft.com/en-us/library/dd233226.aspx).

Here are the videos I took of the event for all attendees that weren't able to make it:

Mike started by describing Discriminated Unions by explaining the syntax for the type which looks like this:

type<type-name> =

| <case1-name> …

Read more… 310 more words, 5 more videos

WPF MVVM with Xaml Type Provider

About a year ago XAML type provider (that now a part of fsharpx project) was born. First of all, happy birthday XAML type provider and thank you everyone who was involved.

Up to XAML type provider release the best option for F# WPF development was to split app into two parts: C# project with all XAML stuff for best tooling support and F# project with all source code. Daniel Mohl have created a project template “F# and C# Win App (WPF, MVVM)” that illustrates this approach end to end (read more about this in his blog).

XAML type provider is an amazing thing that makes available full-featured WPF development completely in F#. Steffen Forkmann has an excellent blog post about its usage “WPF Designer for F#“. It is probably one of my favorite posts about F# at all, it shows a real beauty and excellence of the technology.  This approach was already templated by Daniel Mohl – “F# Empty Windows App (WPF)“.

I think that a natural desire is to have an F# MVVM app using XAML type provider. It can be done by  combining these two templates. At the first step, create a new project from “F# Empty Windows App (WPF)” template, and leave App.fs file without any changes.

module MainApp

open System
open System.Windows
open System.Windows.Controls
open FSharpx

type MainWindow = XAML<"MainWindow.xaml">

let loadWindow() =
   let window = MainWindow()
   window.Root

[<STAThread>]
(new Application()).Run(loadWindow()) |> ignore

Now we need to define a ViewModel for MainWindow. I have reused BaseViewModel and RelayCommand from polyglot approach template.

namespace ViewModels

open System
open System.Windows
open System.Windows.Input
open System.ComponentModel

type ViewModelBase() =
    let propertyChangedEvent = new DelegateEvent<PropertyChangedEventHandler>()
    interface INotifyPropertyChanged with
        [<CLIEvent>]
        member x.PropertyChanged = propertyChangedEvent.Publish
    member x.OnPropertyChanged propertyName = 
        propertyChangedEvent.Trigger([| x; new PropertyChangedEventArgs(propertyName) |])

type RelayCommand (canExecute:(obj -> bool), action:(obj -> unit)) =
    let event = new DelegateEvent<EventHandler>()
    interface ICommand with
        [<CLIEvent>]
        member x.CanExecuteChanged = event.Publish
        member x.CanExecute arg = canExecute(arg)
        member x.Execute arg = action(arg)

type MainViewModel () = 
    inherit ViewModelBase()

    let mutable name = "Noname"
    member x.Name 
        with get () = name
        and set value = name <- value
                        x.OnPropertyChanged "Name"

    member x.OkCommand = 
        new RelayCommand ((fun canExecute -> true), 
            (fun action -> MessageBox.Show(sprintf "Hello, %s" x.Name) |> ignore)) 

The last and probably most tricky part is a XAML. Pay attention to the row number four (local namespace definition). You need to specify assembly part even if your view model located in the same assembly as XAML. It happens because type provider works in another one.

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ViewModels;assembly=FsharpMVVMWindowsApp" 
        Title="MVVM and XAML Type provider" Height="120" Width="300">
    <Window.DataContext>
        <local:MainViewModel></local:MainViewModel>
    </Window.DataContext>
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Label FontSize="16">What is your name?</Label>
        <TextBox Grid.Row="1" FontSize="16" Text="{Binding Name, Mode=TwoWay}"/>
        <Button Grid.Row="2" FontSize="16" Command="{Binding OkCommand}">Ok</Button>
    </Grid>
</Window>

Voila, it works now.

HelloSergey

Neo4jClient new Cypher Start notation & F# Extra Top Level Operators

Some weeks ago a new version of Neo4jClient was released. There were introduced some changes in the Cypher Start notation. The new Start notation for sure has become nicer for C#, but it is still a bit ugly for F#. Recommended way of using new Start notation are anonymous classes that do not supported in F#.

The one reasonable option is to use dictionary-like interface, which in C# looks in the following way:

graphClient
    .Cypher
    .Start(new Dictionary<string, object>
    {
        { "foo", nodeRef },
        { "bar", otherNodeRef }
    });

We need to create IDictionary<string,obj> object somehow. Luckily, F# has a set of Extra Top Level Operators one of which is dict that does exactly what we need. The last thing what we need to do is to box dictionary values to convert them to obj.

dict : seq<'Key * 'Value> -> IDictionary<'Key,'Value> (requires equality)
box : 'T -> obj

Some examples:

let getById (queryObject:'T when 'T :> NeoEntity) =
    client.Cypher
        .Start(dict ["n", box(sprintf "node(%d)" (queryObject.Id))])
        .Return<Node<'T>>("n")
        .Results

let simpleConnection connectionType (target:Node<_>) (source:Node<_>) =
    client.Cypher
        .Start(dict [("n", box(source.Reference)); ("m", box(target.Reference))])
        .CreateUnique(sprintf "n-[:%s]->m" connectionType)
        .ExecuteWithoutResults()

Update: Start notation supports wide range of possible object references:

graphClient
  .Cypher
  .Start(dict [
    ("n1", box "custom");
    ("n2", box nodeRef);
    ("n3", box Node.ByIndexLookup("indexName", "property", "value"));
    ("n4", box Node.ByIndexQuery("indexName", "query"));
    ("r1", box relRef);
    ("moreRels", box [|relRef; relRef2|]);
    ("r2", box Relationship.ByIndexLookup("indexName", "property", "value"));
    ("r3", box Relationship.ByIndexQuery("indexName", "query"));
    ("all", box All.Nodes)
  ])
Follow

Get every new post delivered to your Inbox.