201. Arsitektur Web (Client & Server) & Package net/http
Konsep dasar HTTP Protocol, Client-Server architecture, dan pengenalan package net/http.
Arsitektur Web di Golang
Golang memiliki pendirian "Batteries Included" untuk pengembangan web. Package standar net/http menyediakan HTTP Server & Client kelas produksi tanpa memerlukan framework pihak ketiga.
| Komponen | Peran di Go |
|---|---|
| Client (Browser/Postman) | Mengirim HTTP Request ke Server |
| http.Server | Mendengarkan Port TCP & menerima koneksi HTTP |
| http.Handler | Memproses request dan menulis HTTP Response |
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
fmt.Println("=== NET/HTTP PACKAGE ===")
fmt.Println("Golang memiliki Web Server bawaan yang sangat kencang di package net/http.")
_ = http.Server{}
}
202. HTTP Server & http.ListenAndServe
Menjalankan HTTP Web Server lokal dengan mengonfigurasi host dan port.
Menjalankan HTTP Server
Fungsi http.ListenAndServe("localhost:8080", handler) digunakan untuk menyalakan web server pada address tertentu.
server := http.Server{
Addr: "localhost:8080",
Handler: mux,
}
err := server.ListenAndServe()
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
server := &http.Server{
Addr: "localhost:8080",
}
fmt.Println("Server siap berjalan pada address:", server.Addr)
}
203. Handler & http.HandlerFunc
Membuat fungsi pemroses request HTTP menggunakan interface http.Handler dan adapter HandlerFunc.
Interface http.Handler
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
Kita dapat mengubah fungsi biasa menjadi `http.Handler` menggunakan adapter `http.HandlerFunc`:
var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello Golang Web!")
}
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Halo Pengunjung Web Go!")
}
fmt.Println("HandlerFunc berhasil dikonfigurasi:", handler != nil)
}
204. Multi-Routing dengan http.ServeMux
Mengarahkan berbagai endpoint URL ke handler yang sesuai menggunakan ServeMux.
Pengenalan http.ServeMux
http.ServeMux adalah HTTP request multiplexer bawaan Go. Mux mencocokkan URL dari setiap request yang masuk dengan daftar pola (pattern) yang didaftarkan.
mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/about", aboutHandler)
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Index Page")
})
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello Page")
})
fmt.Println("ServeMux siap dengan 2 endpoint (/ dan /hello).")
}
205. Handling HTTP Request & Method Inspecting
Memeriksa detail informasi HTTP Request seperti Method (GET, POST), URL Path, dan Protocol.
Struktur *http.Request
Objek *http.Request menampung seluruh atribut request yang dikirim client:
r.Method: "GET", "POST", "PUT", "DELETE"r.URL.Path: "/users/profile"r.Header: Map header HTTP
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "/api/users", nil)
fmt.Println("Method:", req.Method)
fmt.Println("Path:", req.URL.Path)
}
206. Testing Web Handler dengan Package httptest
Melakukan unit testing pada HTTP handler tanpa harus menyalakan server fisik di port TCP.
Package net/http/httptest
Go memiliki package khusus untuk menguji handler web secara instan:
req := httptest.NewRequest("GET", "/hello", nil)
recorder := httptest.NewRecorder()
HelloHandler(recorder, req)
res := recorder.Result()
fmt.Println("Status:", res.StatusCode)
Contoh program
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "OK Test")
}
req := httptest.NewRequest("GET", "/", nil)
rec := httptest.NewRecorder()
handler(rec, req)
fmt.Println("Hasil Response Test Body:", rec.Body.String())
}
207. Query Parameter & Multi-Value Query Params
Membaca data parameter dari URL query string (?name=Eko&age=30).
Membaca Query Parameter
Query parameter diambil dari `r.URL.Query()` yang mengembalikan tipe url.Values (map[string][]string):
name := r.URL.Query().Get("name") // Nilai pertama
tags := r.URL.Query()["tags"] // Multi-values slice
Contoh program
package main
import (
"fmt"
"net/url"
)
func main() {
u, _ := url.Parse("http://localhost:8080/search?q=golang&page=2&tags=go&tags=web")
q := u.Query()
fmt.Println("Keyword (q):", q.Get("q"))
fmt.Println("Page:", q.Get("page"))
fmt.Println("Tags (slice):", q["tags"])
}
208. Reading & Writing HTTP Headers
Membaca header request dan menulis header response seperti Content-Type dan Custom Headers.
HTTP Headers
// Membaca Header Request
contentType := r.Header.Get("Content-Type")
// Menulis Header Response (Wajib sebelum WriteHeader/Write)
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Powered-By", "Golang")
Contoh program
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintln(w, `{"message":"Hello JSON"}`)
}
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
handler(rec, req)
fmt.Println("Response Content-Type Header:", rec.Header().Get("Content-Type"))
}
209. Parsing Form Post (URL Encoded & Form Data)
Membaca input data form dari request body POST.
Membaca Data POST Form
err := r.ParseForm()
firstName := r.PostFormValue("first_name")
email := r.PostForm.Get("email")
r.PostFormValue("key") otomatis memanggil r.ParseForm() jika belum dipanggil sebelumnya.
Contoh program
package main
import (
"fmt"
"net/http"
"strings"
)
func main() {
req, _ := http.NewRequest("POST", "/login", strings.NewReader("username=eko&password=secret123"))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.ParseForm()
fmt.Println("Username:", req.PostFormValue("username"))
fmt.Println("Password:", req.PostFormValue("password"))
}
210. Setting HTTP Response Status Code (w.WriteHeader)
Mengirimkan HTTP status code spesifik (200 OK, 400 Bad Request, 404 Not Found, 500 Internal Error).
HTTP Response Code
w.WriteHeader(http.StatusNotFound) // 404 fmt.Fprintln(w, "Halaman Tidak Ditemukan")
Jika w.WriteHeader() tidak dipanggil eksplisit, Go secara otomatis mengirimkan 200 OK saat pertama kali w.Write() dipanggil.
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
fmt.Println("Konstanta Status Code Go:")
fmt.Println("Status OK:", http.StatusOK)
fmt.Println("Status Bad Request:", http.StatusBadRequest)
fmt.Println("Status Not Found:", http.StatusNotFound)
fmt.Println("Status Internal Error:", http.StatusInternalServerError)
}
211. Managing HTTP Cookies (http.Cookie)
Membuat, mengirim, dan membaca Cookie pada web browser client.
Operasi Cookie
// Menulis Cookie
cookie := &http.Cookie{
Name: "X-User-Token",
Value: "abc123token",
Path: "/",
}
http.SetCookie(w, cookie)
// Membaca Cookie
cookie, err := r.Cookie("X-User-Token")
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
c := &http.Cookie{
Name: "session_id",
Value: "xyz987654",
Path: "/",
}
fmt.Printf("Cookie Struct: %s=%s (Path: %s)
", c.Name, c.Value, c.Path)
}
212. Serving Static Files dengan http.FileServer & http.ServeFile
Menyajikan file statis (HTML, CSS, JS, Gambar) ke browser client.
Static File Server
directory := http.Dir("./static")
fileServer := http.FileServer(directory)
// Menghapus prefix URL sebelum meneruskan ke folder
mux.Handle("/static/", http.StripPrefix("/static", fileServer))
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("./static"))
handler := http.StripPrefix("/assets", fs)
fmt.Println("FileServer & StripPrefix terkonfigurasi:", handler != nil)
}
213. HTML Templating Dasar (html/template)
Membuat tampilan HTML dinamis menggunakan package standar html/template.
Package html/template
tmpl, err := template.ParseFiles("./templates/index.gohtml")
tmpl.Execute(w, data)
Package ini secara otomatis menangani escaping karakter HTML sehingga aman dari serangan XSS secara default.
Contoh program
package main
import (
"bytes"
"fmt"
"html/template"
)
func main() {
text := "<h1>Selamat Datang, {{.}}!</h1>"
tmpl, _ := template.New("index").Parse(text)
var buf bytes.Buffer
tmpl.Execute(&buf, "Budi Kurniawan")
fmt.Println("Hasil Render Template:")
fmt.Println(buf.String())
}
214. Passing Dynamic Data ke Template (Struct, Map, Dynamic Field)
Mengirimkan data dinamis berupa Struct atau Map ke dalam template HTML.
Mengirim Data ke Template
type PageData struct {
Title string
Name string
}
tmpl.Execute(w, PageData{Title: "Home", Name: "Eko"})
Di dalam HTML Template, field diakses menggunakan tanda titik: {{.Title}} dan {{.Name}}.
Contoh program
package main
import (
"bytes"
"fmt"
"html/template"
)
type UserProfile struct {
Name string
Role string
}
func main() {
htmlTmpl := "<p>User: {{.Name}} | Role: {{.Role}}</p>"
tmpl, _ := template.New("profile").Parse(htmlTmpl)
var buf bytes.Buffer
tmpl.Execute(&buf, UserProfile{Name: "Eko Khannedy", Role: "Software Engineer"})
fmt.Println(buf.String())
}
215. Template Actions (if, else, range, with)
Menambahkan logika pengondisian (if/else) dan perulangan (range) pada HTML Template.
Sintaks Template Actions
{{if .IsAdmin}}
<p>Admin Dashboard</p>
{{else}}
<p>User Dashboard</p>
{{end}}
{{range $index, $element := .Hobbies}}
<li>{{$index}}: {{$element}}</li>
{{end}}
Contoh program
package main
import (
"bytes"
"fmt"
"html/template"
)
func main() {
src := `Hobi:
{{range .Hobbies}}
- {{.}}
{{end}}`
tmpl, _ := template.New("hobbies").Parse(src)
data := map[string]interface{}{
"Hobbies": []string{"Coding", "Gaming", "Reading"},
}
var buf bytes.Buffer
tmpl.Execute(&buf, data)
fmt.Println(buf.String())
}
216. Template Layout & Reusable Partials
Membuat struktur layout modular (Header, Footer, Content) dengan {{define}} dan {{template}}.
Template Layout
{{define "header"}}
<header><h1>{{.Title}}</h1></header>
{{end}}
{{define "main"}}
{{template "header" .}}
<main><p>Konten Utama</p></main>
{{end}}
Contoh program
package main
import (
"bytes"
"fmt"
"html/template"
)
func main() {
tmpl := template.New("layout")
template.Must(tmpl.New("header").Parse("<header>Header Site</header>
"))
template.Must(tmpl.New("body").Parse("{{template "header" .}}<main>Main Body</main>"))
var buf bytes.Buffer
tmpl.ExecuteTemplate(&buf, "body", nil)
fmt.Println(buf.String())
}
217. Custom Template Functions
Mendaftarkan fungsi buatan sendiri ke dalam HTML Template menggunakan template.FuncMap.
Custom FuncMap
funcMap := template.FuncMap{
"upper": strings.ToUpper,
}
tmpl := template.New("test").Funcs(funcMap)
Penggunaan di HTML: {{upper .Name}}
Contoh program
package main
import (
"bytes"
"fmt"
"html/template"
"strings"
)
func main() {
funcMap := template.FuncMap{
"upper": strings.ToUpper,
}
tmpl, _ := template.New("funcTest").Funcs(funcMap).Parse("Hello {{upper .}}")
var buf bytes.Buffer
tmpl.Execute(&buf, "kodeloka")
fmt.Println(buf.String())
}
218. Template Caching & Performance Optimization
Strategi mem-parse template sekali pada saat inisialisasi aplikasi untuk performa maksimal.
Template Caching
Memanggil template.ParseFiles() di dalam fungsi HTTP Handler akan membaca ulang file dari disk pada setiap request (Sangat Lambat!).
Gunakan template.Must(template.ParseGlob("./templates/*.gohtml")) pada level global saat aplikasi pertama kali menyala.
Contoh program
package main
import (
"fmt"
"html/template"
)
// Template diparse sekali pada level paket/global
var templates = template.Must(template.New("app").Parse("<h1>Cached Template</h1>"))
func main() {
fmt.Println("Template berhasil di-cache pada startup!", templates != nil)
}
219. Keamanan Web: Mencegah XSS (Cross-Site Scripting)
Mekanisme proteksi auto-escaping HTML dan cara aman menangani konten HTML mentah.
Auto Escaping Security
Jika pengguna memasukkan input mengandung script jahat <script>alert('xss')</script>, html/template otomatis mengonversinya menjadi safe entities: <script>...
Jika ingin merender HTML asli tanpa escaping (hanya dari sumber terpercaya), gunakan tipe template.HTML.
Contoh program
package main
import (
"bytes"
"fmt"
"html/template"
)
func main() {
userInput := "<script>alert('hack')</script>"
tmpl, _ := template.New("xss").Parse("Input: {{.}}
")
var buf bytes.Buffer
tmpl.Execute(&buf, userInput)
fmt.Println("Hasil Render Safe Escaped:")
fmt.Println(buf.String())
}
220. HTTP Redirects (http.Redirect)
Mengalihkan halaman pengguna dari satu lokasi URL ke lokasi lain.
Penggunaan http.Redirect
func LoginHandler(w http.ResponseWriter, r *http.Request) {
// Pengalihan ke halaman /dashboard dengan status 303 See Other
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
}
Contoh program
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/login", http.StatusSeeOther)
}
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/old-path", nil)
handler(rec, req)
fmt.Println("Redirect Status Code:", rec.Code)
fmt.Println("Redirect Location Header:", rec.Header().Get("Location"))
}
221. Handling File Upload (r.FormFile)
Menerima dan memproses unggahan file dari form multipart/form-data.
File Upload Handling
file, header, err := r.FormFile("avatar")
if err != nil { return }
defer file.Close()
// Simpan file ke disk
targetFile, _ := os.Create("./uploads/" + header.Filename)
io.Copy(targetFile, file)
Contoh program
package main
import (
"fmt"
"net/http"
)
func main() {
fmt.Println("=== FILE UPLOAD HANDLER ===")
fmt.Println("1. HTML Form: enctype="multipart/form-data"")
fmt.Println("2. Go Handler: file, header, err := r.FormFile("fieldname")")
fmt.Println("3. Simpan dengan io.Copy(destFile, file)")
_ = http.Request{}
}
222. Download File Header (Content-Disposition)
Mengirim file ke pengguna dengan instruksi download otomatis di browser.
File Download
Untuk memaksa browser mengunduh file alih-alih mengabarkannya di layar, set header Content-Disposition:
w.Header().Set("Content-Disposition", "attachment; filename="laporan.pdf"")
http.ServeFile(w, r, "./files/laporan.pdf")
Contoh program
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename="document.txt"")
fmt.Fprintln(w, "Isi dokumen rahasia")
}
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/download", nil)
handler(rec, req)
fmt.Println("Header Content-Disposition:", rec.Header().Get("Content-Disposition"))
}
223. Web Middleware Pattern
Membangun komponen Middleware (Logger, Auth, Recover) dengan membungkus http.Handler.
Pola Middleware di Go
type Middleware func(http.Handler) http.Handler
func LogMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Log Request:", r.Method, r.URL.Path)
next.ServeHTTP(w, r) // Lanjut ke handler berikutnya
})
}
Contoh program
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func SimpleLogMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("[LOG] Incoming Request:", r.URL.Path)
next.ServeHTTP(w, r)
})
}
func main() {
mainHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Core Handler Executed")
})
wrapped := SimpleLogMiddleware(mainHandler)
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/dashboard", nil)
wrapped.ServeHTTP(rec, req)
}