George Petasis: This page contains a small example of how an HTTP REST service can be implemented with Rivet. I don't know if this is the best way to implement such a service, but it works for me (Although my actual implementation uses TclOO). The reason I have created this page, is that the wiki or Rivet site have no examples on REST service implementations.
package require json package require json::write fconfigure stdout -encoding utf-8 ## ## Helper functions, for returning results... ## proc returnJSON {json} { fconfigure stdout -encoding utf-8 ::rivet::headers numeric 200 ::rivet::headers type {application/json; charset=utf-8} ::rivet::headers add Content-Length [string bytelength $json] puts $json };# returnJSON proc returnREDIRECT {{id {}}} { fconfigure stdout -encoding utf-8 ::rivet::headers numeric 303 ## Create a URI for reading the object with this id... set scheme [::rivet::env REQUEST_SCHEME] set host [::rivet::env HTTP_HOST] set uri [::rivet::env DOCUMENT_URI] if {$id eq ""} { ::rivet::headers add Location ${scheme}://${host}/${uri} } else { ::rivet::headers add Location ${scheme}://${host}/${uri}/$id } ::rivet::no_body };# returnREDIRECT ## ## Service methods: these methods actually implement the service operations... ## namespace eval service { proc index {} { ::json::write array \ [read 0] \ [read 1] \ [read 2] };# index proc create {object} { return id };# create proc read {id} { ::json::write object id [::json::write string $id] \ type [::json::write string sample_object] };# read proc update {id object} { puts $object };# update proc delete {id} { };# delete } ## ## Get the request method... ## set method [::rivet::env REQUEST_METHOD] set path [::rivet::env PATH_INFO] ## ## The dispatcher, which examines the uri, and calls the appropriate procedure. ## switch -exact $path { {} - / { ## Path is either empty, or "/": switch -exact $method { GET { ## index # ------------------------------------------------------- # method: GET # path: / # returns: a list of all objects returnJSON [service::index] } POST { ## create # ------------------------------------------------------- # method: POST # path: / # receives: an object, sent with Content-Type: application/json # returns: 303 SEE OTHER redirect to the appropriate read endpoint returnREDIRECT [service::create [::rivet::raw_post]] } default { ## Invalid method/post combination. Return an error ::rivet::headers type {text/plain; charset=utf-8} ::rivet::headers numeric 500 ;# HTTP 500: internal server error if {$path eq ""} {set path /} puts "invalid method \"$method\" for endpoint \"$path\":" puts " valid methods are:" puts " GET: lists all objects" puts " POST: creates a new object" ::rivet::abort_page } } } default { ## Path is: /<object id> switch -glob $path { /*/ {set id [string range $path 1 end-1]} default {set id [string range $path 1 end]} } switch -exact $method { GET { ## read # ------------------------------------------------------- # method: GET # path: /<id> # returns: an object returnJSON [service::read $id] } PUT { ## update # ------------------------------------------------------- # method: PUT # path: /<id> # receives: a (partial) object, sent with Content-Type: application/json # returns: 303 SEE OTHER redirect to the appropriate read endpoint service::update $id [::rivet::raw_post] returnREDIRECT ;# No need to specify an id, the endpoint is the same } DELETE { ## delete # ------------------------------------------------------- # method: DELETE # path: /<id> # returns: 204 NO CONTENT, and -- obviously -- no content service::delete $id ::rivet::headers numeric 204 ::rivet::no_body } default { ## Invalid method/post combination. Return an error headers type {text/plain; charset=utf-8} headers numeric 500 ;# HTTP 500: internal server error if {$path eq ""} {set path /} puts "invalid method \"$method\" for endpoint \"$path\":" puts " valid methods are:" puts " GET: returns the object with id: \"$id\"" puts " PUT: updates the object with id: \"$id\"" puts " DELETE: deletes the object with id: \"$id\"" ::rivet::abort_page } } } }