Page
Library
Module
Module type
Parameter
Class
Class type
Source
WtrSource'a t represents a Trie based router. Pretty printing/debugging a router
'c route is a uri and its handler. 'c represents the value returned by the handler.
('a, 'b) uri represents a route URI - both the path and query, e.g. /home/about/, /home/contact, /home/contact?name=a&no=123 etc.
method' represents HTTP request methods. It can be used as part of a uri in %wtr ppx.
create routes creates a router from a list of routes. Values of routes are created by %wtr ppx.
A full example demonstrating creating a router, route and route handlers:
module Fruit = struct
type t = Apple | Orange | Pineapple
let t : t Wtr.decoder =
Wtr.create_decoder ~name:"fruit" ~decode:(function
| "apple" -> Some Apple
| "orange" -> Some Orange
| "pineapple" -> Some Pineapple
| _ -> None )
end
(* Route handlers. *)
let about_page = "about page"
let prod_page i = "Int page. number : " ^ string_of_int i
let float_page f = "Float page. number : " ^ string_of_float f
let contact_page nm num =
"Contact. Hi, " ^ nm ^ ". Num " ^ string_of_int num
let product1 name id q =
Format.sprintf "Product1 %s. Id: %d. q = %b" name id q
let product2 name id = Format.sprintf "Product2 %s. Id: %d." name id
let fruit_page = function
| Fruit.Apple -> "Apples are juicy!"
| Orange -> "Orange is a citrus fruit."
| Pineapple -> "Pineapple has scaly skin"
let faq category_id =
let category_name =
match category_id with
| 1 -> "products"
| 2 -> "insurance"
| 3 -> "returns"
| _ -> "unknown"
in
"FAQ page for category : " ^ category_name
let router =
Wtr.(
create
[ {%wtr| get,post,head,delete ; /home/about/ |} about_page
; {%wtr| head,delete ; /home/:int/ |} prod_page
; {%wtr| get,post ; /home/:float/ |} float_page
; {%wtr| get; /contact/*/:int |} contact_page
; {%wtr| get; /product/:string?section=:int&q=:bool |} product1
; {%wtr| get; /product/:string?section=:int&q1=yes |} product2
; {%wtr| get; /fruit/:Fruit |} fruit_page
; {%wtr| GET; /faq/:int/** |} faq ])match method' uri t matches a route to a given uri and method', executes its handler and returns the computed value. None is returned if both uri and method' are not matched.
Examples of calling match' and its results:
let () =
Format.(fprintf std_formatter "@.@.====Router Match Results====@.") ;
[ Wtr.match' `GET "/home/100001.1/" router
; Wtr.match' `DELETE "/home/100001/" router
; Wtr.match' `GET "/home/about/" router
; Wtr.match' `GET "/product/dyson350?section=233&q=true" router
; Wtr.match' `GET "/product/dyson350?section=2&q=false" router
; Wtr.match' `GET "/product/dyson350?section=2&q1=yes" router
; Wtr.match' `GET "/product/dyson350?section=2&q1=no" router
; Wtr.match' `GET "/fruit/apple" router
; Wtr.match' `GET "/fruit/orange" router
; Wtr.match' `GET "/fruit/pineapple" router
; Wtr.match' `GET "/fruit/guava" router
; Wtr.match' `GET "/faq/1/" router
; Wtr.match' `GET "/faq/1/whatever" router
; Wtr.match' `GET "/faq/2/whateasdfasdfasdf" router ]
|> List.iteri (fun i -> function
| Some s -> Printf.printf "%3d: %s\n" (i + 1) s
| None -> Printf.printf "%3d: None\n" (i + 1) )The match call results in the following results:
====Router Match Results====
1: Float page. number : 100001.1
2: Int page. number : 100001
3: about page
4: Product1 dyson350. Id: 233. q = true
5: Product1 dyson350. Id: 2. q = false
6: Product2 dyson350. Id: 2.
7: None
8: Apples are juicy!
9: Orange is a citrus fruit.
10: Pineapple has scaly skin
11: None
12: FAQ page for category : products
13: FAQ page for category : products
14: FAQ page for category : insuranceSpecifying a URI in a %wtr ppx follows the following syntax:
wtr uri spec = http methods separated by comma ';' http uri
A URI in a %wtr ppx is syntactically and sematically a HTTP URI with the addition of decoders and some some useful additions listed below.
** - Full spat operator matches any/all path following a full splat. For example in /home/** matches the following uri paths, /home/about/, home/contact, /home/product etc. Full splat must be the last component of an uri. It is an error to specify other uri path component after full splat operator.* - A wildcard operator matches any text appearing on the path component position. For example, uri /home/*/page1 matches the following /home/23/page1, /home/true/page1, /home/234.4/page1 etc. The semantics of wildcard operator is the same as using :string decoder in a uri, i.e. it affects the route handler function signature./ - A trailing slash ensures that Wtr will match a trailing / in a uri. For example, uri /home/about/ matches /home/about/ but not /home/about.Wtr provides the following built in decoders that can be used as when specifying wtr URI in {%wtr| |} ppx:
:int - decodes a int:int32 - decodes a int32:int64 - decodes a int64:float - decodes a float or int:bool - decodes a bool:string - decodes a stringThe built-in decoders can be used as follows:
{%wtr|get; /home/:int |}, {%wtr| /home/:bool |}
Wtr also supports creating custom, user defined decoders. The convention for user defined decoders is as follows:
It should be defined in a module. The module should define a type called t and a value called t which returns t Wtr.decoder.
Example of defining custom decoder:
module Fruit = struct
type t = Apple | Orange | Pineapple
let t : t Wtr.decoder =
Wtr.create_decoder ~name:"fruit" ~decode:(function
| "apple" -> Some Apple
| "orange" -> Some Orange
| "pineapple" -> Some Pineapple
| _ -> None )
endThe custom decoder then can be used in %wtr ppx as follows,
{%wtr| get ; /fruit/:Fruit |} fruit_page
Usage of decoders in a URI directly affect the function signature of a route handler. For e.g.
/home/:int/:bool expects a route handler as fun (i:int) (b:bool) -> ..../home/:string expects a route handler as (fun (s:string) -> ...)create_decoder ~name ~decode creates a user defined decoder uri component. name is used during the pretty printing of uri.
pp fmt t pretty prints router routes. This can be useful for debugging router/routing issues as it displays hierarchially possible routes a matching engine may take in matching a given uri and method.
HTTP method names are capitalized.
Printing the router from the example givn in create method pretty prints the following:
GET
/home
/about
/
/:float
/
/contact
/:string
/:int
/product
/:string
/section
/:int
/q
/:bool
/q1
/yes
/fruit
/:fruit
/faq
/:int
/**
POST
/home
/about
/
/:float
/
HEAD
/home
/about
/
/:int
/
DELETE
/home
/about
/
/:int
/