2017-12-06

Apache Rivet: status code and content type

最近在學習 Vue.js,所以有做一些 server 端的測試。寫法大致上是這樣:

Vue.js + Axios  -> send POST request to server -> get response

也就是所謂的前後端分離。在這個情況下,server 端(例如 Apache HTTP server + Apache Rivet 的組合)就變成比較像 web API 的部份。

XML 的範例可以看 Apache Rivet 的手冊:
#
# Ajax query servelet: a pseudo database is built into the dictionary 'composers' with the
# purpose of emulating the role of a real data source. 
# The script answers with  2 types of responses: a catalog of the record ids and a database 
# entry matching a given rec_id. The script obviously misses the error handling and the
# likes. Just an example to see rivet sending xml data to a browser. The full Tcl, JavaScript
# and HTML code are available from http://people.apache.org/~mxmanghi/rivet-ajax.tar.gz

# This example requires Tcl8.5 or Tcl8.4 with package 'dict' 
# (http://pascal.scheffers.net/software/tclDict-8.5.2.tar.gz)
# 

# A pseudo database. rec_id matches a record in the db

set composers [dict create  \
                1 {first_name Claudio middle_name "" last_name Monteverdi   \
                    lifespan 1567-1643 era Renaissance/Baroque}             \
                2 {first_name Johann middle_name Sebastian last_name Bach   \
                    lifespan 1685-1750 era Baroque }                        \
                3 {first_name Ludwig middle_name "" last_name "van Beethoven" \
                    lifespan 1770-1827 era Classical/Romantic}              \
                4 {first_name Wolfgang middle_name Amadeus last_name Mozart \
                    lifespan 1756-1791 era Classical }                      \
                5 {first_name Robert middle_name "" last_name Schumann      \
                    lifespan 1810-1856 era Romantic} ]

# we use the 'load' argument in order to determine the type of query
#
# load=catalog:         we have to return a list of the names in the database
# load=composer&amp;res_id=<id>: the script is supposed to return the record
#               having <id> as record id

if {[::rivet::var exists load]} {

# the xml declaration is common to every message (error messages included)

    set xml "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
    switch [::rivet::var get load] {
        catalog {
            append xml "<catalog>\n"
            foreach nm [dict keys $composers] {
                set first_name  [dict get $composers $nm first_name]
                set middle_name [dict get $composers $nm middle_name]
                set last_name   [dict get $composers $nm last_name]
                append xml "    <composer key=\"$nm\">$first_name "
                if {[string length [string trim $middle_name]] > 0} {
                    append xml "$middle_name "
                }
                append xml "$last_name</composer>\n"
            }
            append xml "</catalog>\n"
        }
        composer {
            append xml "<composer>\n"
            if {[::rivet::var exists rec_id]} {
                set rec_id [::rivet::var get rec_id]
                if {[dict exists $composers $rec_id]} {
                    foreach {k v} [dict get $composers $rec_id] {
                        append xml "<$k>$v</$k>\n"
                    }
                }
            }
            append xml "</composer>\n"
        }
    }

# we have to tell the client this is an XML message. Failing to do so
# would result in an XMLResponse property set to null

    ::rivet::headers type "text/xml"
    ::rivet::headers add Content-Length [string length $xml]
    puts $xml
}

只要知道怎麼拿取 request 的資料跟如何回應,就可以組成 web API。下面是我快速驗證的 prototype:
<?
    package require rl_json
    package require tdbc::postgres

    ::rivet::load_response response

    if {[info exists response(sqlstring)]==1} {
        set sqlstring $response(sqlstring)
    }

    if {[info exists response(sqlstring)]==0 || [info exists sqlstring]==0} {
        ::rivet::headers  numeric 400
    } else {
        ::rivet::headers numeric 200
        ::rivet::headers type application/json

        tdbc::postgres::connection create db -user danilo -password danilo -port 5432 -database danilo

        set stmt [db prepare $sqlstring]
        set resultset [$stmt execute]

        set cols [$resultset columns]
        set param_colitem [list]
        foreach col $cols {
            lappend param_colitem "string \"$col\""
        }
        rl_json::json set data "column" [rl_json::json new "array" {*}$param_colitem]

        set param_rowitems [list]
        $resultset foreach -as lists result {
            set param_rowitem [list]
            lappend param_rowitem "array"
            foreach row $result {
                lappend param_rowitem "string \"$row\""
            }

            lappend param_rowitems $param_rowitem
        }

        $resultset close
        $stmt close
        db close

        set data [rl_json::json set data "row" [rl_json::json new "array" {*}$param_rowitems]]
        puts $data
    }
?>

Apache Rivet 有自己的 Database API,可是因為我只是要寫一個測試用的程式,所以直接使用 TDBC。這個範例是送出 SQL statement,然後 POST 到 server 上,server 執行以後回傳結果。

因為並沒有做使用者資料驗證和檢查,這個測試程式請不要在測試目的以外的地方使用,這只是用來說明,我們可以使用 Apache Rivet 用來實作 web api。

沒有留言: