Routing
Jika ada lebih dari satu endpoint, kita membutuhkan routing
File main.go akan diubah agar parameter server, yaitu http.Server.Handler akan diarahkan ke file routing yang mengimplementasikan interface http.Handler
// parameter server
server := http.Server{
Addr: os.Getenv("APP_PORT"),
Handler: routing.API(db, log),
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
}Berikut full kode main.go mengikuti perubahan parameter Handler
package main
import (
"context"
"essentials/libraries/config"
"essentials/libraries/database"
"essentials/routing"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
if _, ok := os.LookupEnv("APP_ENV"); !ok {
config.Setup(".env")
}
// =========================================================================
// Logging
log := log.New(os.Stdout, "Essentials : ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
if err := run(log); err != nil {
log.Printf("error: shutting down: %s", err)
os.Exit(1)
}
}
func run(log *log.Logger) error {
// =========================================================================
// App Starting
log.Printf("main : Started")
defer log.Println("main : Completed")
// =========================================================================
// Start Database
db, err := database.Open()
if err != nil {
return fmt.Errorf("connecting to db: %v", err)
}
defer db.Close()
// parameter server
server := http.Server{
Addr: os.Getenv("APP_PORT"),
Handler: routing.API(db, log),
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
}
serverErrors := make(chan error, 1)
// mulai listening server
go func() {
log.Println("server listening on", server.Addr)
serverErrors <- server.ListenAndServe()
}()
// Membuat channel untuk mendengarkan sinyal interupsi/terminate dari OS.
// Menggunakan channel buffered karena paket signal membutuhkannya.
shutdown := make(chan os.Signal, 1)
signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM)
// Mengontrol penerimaan data dari channel,
// jika ada error saat listenAndServe server maupun ada sinyal shutdown yang diterima
select {
case err := <-serverErrors:
return fmt.Errorf("Starting server: %v", err)
case <-shutdown:
log.Println("caught signal, shutting down")
// Jika ada shutdown, meminta tambahan waktu 5 detik untuk menyelesaikan proses yang sedang berjalan.
const timeout = 5 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Printf("main : Graceful shutdown did not complete in %v : %v", timeout, err)
if err := server.Close(); err != nil {
return fmt.Errorf("could not stop server gracefully: %v", err)
}
}
}
return nil
}Kode di atas error karena kita belum mebuat file routing/route.go yang menghandle routing
Routing Menggunakan ServeMux
Golang sudah mempunyai routing bawaan yaitu http.ServeMux
Kita akan buat routing yang mengimplementasikan interface http.Handler. Buat file routing/route.go yang berisi :
Kode di atas jika dijalankan akan error
*app does not implement http.Handler (missing ServeHTTP method)karena func API return-nya adalah interface http.Handler namun nyatanya yang direturn adalah *appInterface http.Handler mempunyai method abstract bernama ServeHTTP
Karena itu, *app harus mengimplementasikan interface http.Handler dengan membuat method konkret ServeHTTP
Penggunaan HTTP Method dalam ServeMux
HandleFunc tidak memperhatikan http method, sehingga baik method GET, POST, PUT, DELETE akan mengeksekusi handler users.List
Untuk mendukung method kita perlu merubah fungsi HandleFunc di atas.
Tambahkan method lainnya di file controllers/users.go
Routing dengan httprouter
http.ServeMux sangat handal performance-nya. Namun http.ServeMux tidak support pattern dalam routing url, sehingga terkesan tidak modern. Padahal umumnya sekarang untuk routing ResT kita menggunakan pattern, seperti :
Karena itulah terpaksa kita harus membuat routing sendiri atau memilih menggunakan library routing lain yang sudah ada. Salah satunya adalah httprouter
Performance httprouter sangat handal -- lihat benchmark --
Kekurangan httprouter adalah tidak mendukung standard http.Handler dan tidak ada middleware
Tapi kita bisa membuat middleware sendiri dan mengubah sedikit agar httprouter mendukung standar http.Handler.
Ubah file routing/route.go menjadi :
Ubah file controllers/users.go menjadi :
Routing ini sudah berjalan dengan baik. Namun agar kode routing lebih dimaintenance, file routing/route.go akan dipecah menjadi dua file. Kode-kode yang mengatur tentang app akan dijadikan library tersendiri.
Ubah file routing/route.go menjadi :
Buat file libraries/api/app.go yang berisi :
Standard http.Handler
httprouter tidak menggunakan standard http handle dengan 3 params, yaitu : http.ResponseWriter, *http.Request, dan httprouter.Params. Pada sub bab ini, kita akan mengubah httprouter.Handle menggunakan standard http.Handle
Buat
type Handler func(http.ResponseWriter, *http.Request)di file libraries/api/app.goBuat
type Ctx stringdi file libraries/api/app.go untuk dijadikan key saat memindahkan httrouter.params ke contextUbah parameter httprouter.Handle menjadi http.Handle di fungsi App.Handle di file libraries/api/app.go
Keseluruhan file libraries/api/app.go setelah mengalami perubahan adalah sebagai berikut:
Ubah kembali file controllers/users.go agar sesuai dengan standar http.Handle
Handle CORS
Untuk handle CORS tambahkan header seperti berikut di file libraries/api/app.go
File routing juga memanggil method HandleCors
Last updated
Was this helpful?