Search code examples
f#suave

What is the simplest way to check that an incoming request contains a certain header value


I have a simple Suave.io server in the form:

let Ok o = o |> JsonConvert.SerializeObject |> Successful.OK
let NotOk o = o |> JsonConvert.SerializeObject |> RequestErrors.BAD_REQUEST

type Result<'T> =
| Success of 'T
| Failure of string

let DoThing someParam anotherParam =
    let stats = Success(999) // <- business code here
    match stats with
    | Success s -> s |> Ok
    | Failure m -> m |> NotOk
...
        let app =
            choose
                [ GET >=> choose
                    [
                        pathScan "/someroute/%i/%i" (fun (p1, p2) -> 
                                DoThing p1 p2)
                    ] 
                ]
        startWebServer config app
        0

I would like to check that the request contains a header with a certain name and value and return NotOk when it is absent or incorrect. What's the simplest way to achieve this?

I'm a newcomer to Suave.io's compositional style.


Solution

  • I think that this would do what you need:

    #r "Newtonsoft.Json.dll"
    #r "Suave.dll"
    #r "Suave.Testing.dll"
    #r "System.Net.Http.dll"
    #r "Fuchu.dll"
    
    open System.Net
    open System.Net.Http
    
    open Suave
    open Suave.Operators
    open Newtonsoft.Json
    open Suave.Filters
    open Suave.Testing
    
    let Ok o = o |> JsonConvert.SerializeObject |> Successful.OK
    let NotOk o = o |> JsonConvert.SerializeObject |> RequestErrors.BAD_REQUEST
    
    type Result<'T> =
    | Success of 'T
    | Failure of string
    
    let DoThing someParam anotherParam =
        let stats = Success(999) // <- business code here
        match stats with
        | Success s -> s |> Ok
        | Failure m -> m |> NotOk
    
    let checkHeader ctx =
        asyncOption {
            match "key" |> ctx.request.header with
            | Choice1Of2 k1 ->
                printfn "Header k1: %s" k1
                return ctx
            | Choice2Of2 k2 ->
                printfn "Header k2: %s" k2
                return { ctx with
                            response = { ctx.response with
                                            status = HTTP_400 } } }
    
    let app =
        choose
            [ GET >=> choose
                [
                    pathScan "/someroute/%i/%i" (fun (p1, p2) ->
                            DoThing p1 p2)
                    >=> checkHeader
                ]
            ]
    
    let emptyCont = new ByteArrayContent([||]) |> Some
    
    runWith defaultConfig app
    |> reqResp HttpMethod.GET 
               "/someroute/1/2" "" 
               None 
               None 
               DecompressionMethods.None
               id
               id
    

    Make sure that you have the required libs in the same directory as this script and you can run it. Because there is no key header, the result will be a BAD_REQUEST with content 999.

    The checkHeader does the trick.