Pustaka
Web dan data
Server HTTP, template, cookie dan sesi, WebSocket, klien HTTP, dan SQLite.
Semua yang dibutuhkan aplikasi web ada di namespace dok:: dan http::, tanpa pustaka tambahan.
Server
package main
pub fn main() {
let r = dok::router_new()
dok::router_get(r, "/", |req| dok::html("<h1>Halo dari JRG</h1>"))
dok::router_get(r, "/sapa/:nama", |req| dok::text("halo, " + req.params.nama))
dok::router_get(r, "/api/kuadrat", |req| {
let n = int(dok::query_get(req.query, "n") ?? "0")
return dok::json_response({"n": n, "kuadrat": n * n})
})
dok::router_post(r, "/gema", |req| dok::text(req.body))
dok::router_static(r, "/aset", "./publik")
dok::serve(r, 8080)
}$ curl localhost:8080/sapa/Ani
halo, Ani
$ curl "localhost:8080/api/kuadrat?n=12"
{"kuadrat":144,"n":12}dok::serve memblokir dan menangani tiap koneksi di thread-nya sendiri. Argumen ketiga mengatur alamat (bawaannya 0.0.0.0; pakai "127.0.0.1" supaya cuma bisa diakses dari mesin sendiri).
Request (req) | Isi |
|---|---|
method, path | "GET", "/sapa/Ani" |
params | bagian :nama dari pola rute, dibaca req.params.nama |
query | pasangan dari ?a=1&b=2; ambil dengan dok::query_get(req.query, "a") |
headers | ambil dengan dok::header_get(req.headers, "Content-Type") |
body, body_bytes | badan sebagai teks; kalau bukan teks UTF-8, body bernilai nil dan isinya di body_bytes |
Handler boleh mengembalikan teks (jadi 200 text/plain), hasil dok::text, dok::html, dok::json_response, dok::redirect, dok::response(kode, isi), dok::binary(byte), atau Result (Err jadi 500).
Middleware
dok::router_use berjalan sebelum rute dicari; mengembalikan nil berarti lanjut, mengembalikan respons berarti berhenti di situ. dok::router_after berjalan sesudah handler dan boleh mengubah responsnya.
dok::router_use(r, |req| {
if str::starts_with(req.path, "/admin") && dok::cookie_get(req, "sesi").is_none() {
return dok::response(401, "perlu masuk dulu")
}
return nil
})
dok::router_after(r, |req, resp| dok::header_set(resp, "X-Frame-Options", "DENY"))Template
dok::render mengisi {{ nama }} dan meloloskan HTML-nya, jadi isian dari pengguna aman ditampilkan. {{& nama }} menyisipkan apa adanya. Ada juga {{#if}}…{{else}}…{{/if}} dan {{#each}}…{{/each}}.
let t = "<h1>Halo {{nama}}</h1><ul>{{#each item}}<li>{{@index}}. {{.}}</li>{{/each}}</ul>"
match dok::render(t, {"nama": "<Ani>", "item": ["kopi", "teh"]}) {
Ok(html) => wok(html),
Err(e) => wok("gagal:", e),
}<h1>Halo <Ani></h1><ul><li>0. kopi</li><li>1. teh</li></ul>dok::render_file(berkas, data) membaca templatnya dari berkas.
Cookie, sesi, dan sandi
let KUNCI = "kunci-sesi-yang-panjang-dan-acak"
let cookie = dok::session_encode({"user": "budi"}, KUNCI)
match dok::session_decode(cookie, KUNCI) {
Ok(d) => wok("masuk sebagai", d.user),
Err(e) => wok("ditolak:", e),
}
let simpanan = dok::password_hash("sandi123")
wok(dok::password_verify("sandi123", simpanan), dok::password_verify("tebakan", simpanan))masuk sebagai budi
true false- Sesi ditandatangani, tidak dienkripsi: isinya bisa dibaca siapa pun, tapi tidak bisa diubah tanpa kuncinya. Simpan kuncinya di variabel lingkungan, bukan di kode.
password_hashmemakai PBKDF2-HMAC-SHA256 dengan garam acak per sandi.dok::cookie_set(resp, nama, nilai, opsi)memasang cookie denganHttpOnlydanSameSite=Laxsecara bawaan;dok::cookie_get(req, nama)membacanya.
WebSocket
Rute WebSocket hidup di router yang sama dengan rute HTTP biasa.
dok::router_ws(r, "/gema", |koneksi, req| {
while true {
match dok::ws_recv(koneksi) {
Some(pesan) => dok::ws_send(koneksi, "gema: " + pesan),
None => { break }
}
}
})ws_recv menunggu pesan berikutnya dan menjawab None saat koneksinya ditutup. Pesan biner datang sebagai array byte.
Klien HTTP
match http::get("https://api.github.com/repos/Kodok-Kejepit/jrg") {
Ok(r) => wok(r.status, json::parse(r.body)["stargazers_count"]),
Err(e) => wok("gagal:", e),
}
http::post("https://contoh.org/api", json::stringify({"a": 1}), "application/json")
http::unduh("https://contoh.org/besar.zip", "besar.zip")http::get, post, put, delete, dan request menjawab Result berisi status, headers, body, dan ok. HTTPS memakai libssl milik sistem. http::unduh menulis langsung ke berkas, jadi aman untuk unduhan besar.
SQLite
Driver SQLite dimuat dari libsqlite3 milik sistem. dok::db_tersedia() memberi tahu apakah pustakanya ada.
let db = dok::db_open(":memory:")?
dok::db_exec(db, "CREATE TABLE murid(id INTEGER PRIMARY KEY, nama TEXT, nilai REAL)")?
dok::db_exec(db, "INSERT INTO murid(nama, nilai) VALUES (?, ?)", ["budi", 8.5])?
dok::db_exec(db, "INSERT INTO murid(nama, nilai) VALUES (?, ?)", ["ani", 9.25])?
for _, m in dok::db_query(db, "SELECT nama, nilai FROM murid ORDER BY nilai DESC")? {
wok(m.nama, m.nilai)
}
let jahat = "budi'; DROP TABLE murid; --"
wok(dok::db_query(db, "SELECT * FROM murid WHERE nama = ?", [jahat])?)
dok::db_close(db)ani 9.25
budi 8.5
[]Nilai selalu diikat lewat ?, jadi teks dari pengguna tidak pernah dibaca sebagai perintah SQL. Satu baris hasil adalah dict, dan kolomnya dibaca dengan namanya.
Jaringan tingkat rendah
dok::tcp_listen, tcp_accept, tcp_connect, tcp_send, tcp_recv_line untuk TCP, dan dok::udp_bind, udp_send_to, udp_recv_from untuk UDP. dok::log_info dan kawan-kawannya mencetak log bertingkat, dengan warna di terminal.