How to get a query parameter in F# Saturn Framework?

1.5k Views Asked by At

Say we have this web server to handle requests:

let webApp = scope {
    get  "/api/zoo/animals/"    (getAllAnimals())
    getf "/api/zoo/animals/%s"  getAnimalInfo
}

This syntax is described in docs and demoed in the example.

Now, what if I want to have a param in the url query, e.g. to filter the results?

http://localhost:8080/api/zoo/animals?type=mammals

This does not do anything:

getf "/api/zoo/animals?type=%s" getAnimalsByType
3

There are 3 best solutions below

0
On BEST ANSWER

A way to go is to use function GetQueryStringValue of the context. It returns Result, a struct DU.

So you stay with initial signature (just remove the trailing slash):

get "/api/zoo/animals" (getAnimals())

And you have

let getAnimals() : HttpHandler =
    fun _ ctx -> task { 
        let animalTypeFromQuery = ctx.GetQueryStringValue "type"
        let animalType =
            match animalTypeFromQuery with
            | Ok t    -> Some t
            | Error _ -> None
        ...
    }

I do not know if this is the official practice, I found this practice in some F# github repos.

0
On

See the example here:

https://github.com/giraffe-fsharp/Giraffe/blob/master/DOCUMENTATION.md#query-strings

It shows how to bind the data from a query string so you don't have to use GetQueryStringValue

In your case I think something like this might work.

[<CLIMutable>]
type AnimalType =
    { type : string }

let animal (next : HttpFunc) (ctx : HttpContext) =
    // Binds the query string to a Car object
    let animal = ctx.BindQueryString<AnimalType>()

    // Sends the object back to the client
    Successful.OK animal next ctx

let web_app  =

    router {    
        pipe_through (pipeline { set_header "x-pipeline-type" "Api" })
        post "/api/animal" animal
    }
0
On

struggled with POST parameters in Gigraff POST

#light "off"
open System.Text.RegularExpressions
open System.IO
open System.Text
open Microsoft.AspNetCore.Http
open Giraffe
open FSharp.Data.Sql
open Giraffe.ViewEngine

let indexView = createPage ("test post") [
...
   let row i d = tr [] [
        td [] [ str (string(i)) ];
        td [] [ str d ]] in
   let db_ctx = mssql.GetDataContext() in
   let rows = [ for r in db_ctx.Dbo.Data do 
           (row r.Id r.Data) done ] in
...  
   form [_method "POST"; _action "/"; _enctype "multipart/form-data"] [
       button [] [str "Add row!" ]  
       input [ _type "text"; _name "Id"]  
       input [ _type "text"; _name "Data"]
   ...
       div[] rows

]

and

  let chk s = not (String.IsNullOrEmpty s)
  let indexHandler : HttpHandler =
    fun (next : HttpFunc) (ctx : HttpContext) -> task { return! (
         if ctx.Request.ContentType.StartsWith( "multipart/form-data") then begin 
           let id = (ctx.Request.Form.Item("Id").ToString()) in
           let data = (ctx.Request.Form.Item("Data").ToString()) in
           if chk id && chk data 
              && not (Regex.IsMatch( id,"[^\\d]"))  then begin
              let db_ctx = mssql.GetDataContext() in
              db_ctx.Dbo.Data.Create( int(id),data) |> ignore;
              db_ctx.SubmitUpdates();
           end;
         end;
         htmlView indexView
      ) next ctx
   }

and

GET >=> route "/" >=> indexHandler;
POST >=> route "/" >=> indexHandler;