package main
import (
"encoding/json"
"fmt"
"net/http"
)
type Product struct {
Name string `json:"name"`
Price int `json:"price"`
Stock int `json:"stock"`
}
var products []Product
func main() {
http.HandleFunc("/product", GetProductsHandler)
http.HandleFunc("/product/create", CreateProductHandler)
http.HandleFunc("/product/update", UpdateProductHandler)
http.HandleFunc("/product/delete", DeleteProductHandler)
http.ListenAndServe(":8080", nil)
}
func GetProductsHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// Handle GET request, return list of products
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(products)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
func CreateProductHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
// Handle POST request, parse request body and validate
var product Product
err := json.NewDecoder(r.Body).Decode(&product)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Invalid request body")
return
}
// Basic validation
if product.Name == "" || product.Price <= 0 || product.Stock < 0 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Invalid product data")
return
}
// Save the product to the list
products = append(products, product)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(product)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
func UpdateProductHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "PUT" {
// Handle PUT request, parse request body and validate
var updatedProduct Product
err := json.NewDecoder(r.Body).Decode(&updatedProduct)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Invalid request body")
return
}
// Find and update the product in the products list
var found bool
for i, p := range products {
if p.Name == updatedProduct.Name {
products[i] = updatedProduct
found = true
break
}
}
if !found {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Product not found")
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(updatedProduct)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
func DeleteProductHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "DELETE" {
// Parse product name from URL query parameter
productName := r.URL.Query().Get("name")
if productName == "" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Product name is required")
return
}
// Find and remove the product from the products list
var found bool
for i, p := range products {
if p.Name == productName {
products = append(products[:i], products[i+1:]...)
found = true
break
}
}
if !found {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Product not found")
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Product deleted successfully")
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
Penjelasan:
GetProductsHandler: Menghandle permintaan GET untuk mendapatkan daftar produk. Handler ini hanya mengizinkan metode GET.
CreateProductHandler: Menghandle permintaan POST untuk membuat produk baru. Produk dibaca dari badan permintaan dan divalidasi sebelum disimpan dalam daftar produk.
UpdateProductHandler: Menghandle permintaan PUT untuk memperbarui produk. Produk yang diperbarui dibaca dari badan permintaan dan dicari dalam daftar produk. Jika ditemukan, produk tersebut diperbarui.
DeleteProductHandler: Menghandle permintaan DELETE untuk menghapus produk berdasarkan nama produk (dalam parameter query URL). Jika produk ditemukan, itu dihapus dari daftar produk.
Semua fungsi handler berada dalam fungsi yang terpisah untuk menjaga keterpisahan antara operasi CRUD, membuat kode lebih mudah dibaca, dimengerti, dan dikelola. Semua handler memeriksa metode HTTP yang diterima dan merespons dengan status dan data yang sesuai.
Untuk memisahkan controller ke dalam folder dan file terpisah, Anda dapat mengatur struktur proyek seperti berikut:
Berikut adalah contoh implementasi controller di file products.go:
products.go:
package Controllers
import (
"encoding/json"
"fmt"
"net/http"
)
type Product struct {
Name string `json:"name"`
Price int `json:"price"`
Stock int `json:"stock"`
}
var products []Product
func GetProductsHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
// Handle GET request, return list of products
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(products)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
func CreateProductHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
// Handle POST request, parse request body and validate
var product Product
err := json.NewDecoder(r.Body).Decode(&product)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Invalid request body")
return
}
// Basic validation
if product.Name == "" || product.Price <= 0 || product.Stock < 0 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Invalid product data")
return
}
// Save the product to the list
products = append(products, product)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(product)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
func UpdateProductHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPut {
// Handle PUT request, parse request body and validate
var updatedProduct Product
err := json.NewDecoder(r.Body).Decode(&updatedProduct)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Invalid request body")
return
}
// Find and update the product in the products list
var found bool
for i, p := range products {
if p.Name == updatedProduct.Name {
products[i] = updatedProduct
found = true
break
}
}
if !found {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Product not found")
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(updatedProduct)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
func DeleteProductHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodDelete {
// Parse product name from URL query parameter
productName := r.URL.Query().Get("name")
if productName == "" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Product name is required")
return
}
// Find and remove the product from the products list
var found bool
for i, p := range products {
if p.Name == productName {
products = append(products[:i], products[i+1:]...)
found = true
break
}
}
if !found {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Product not found")
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Product deleted successfully")
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "Method not allowed")
}
}
Dalam contoh ini, handler-handler telah dipindahkan ke dalam file products.go di dalam folder Controllers. Anda dapat mengimpor file ini di main.go dengan mengganti kode handler yang telah ada dengan pemanggilan fungsi dari file products.go:
Pastikan import path sesuai dengan struktur direktori proyek Anda. Dengan pengaturan ini, handler-handler terpisah dalam file products.go di dalam folder Controllers akan memisahkan logika aplikasi Anda menjadi komponen-komponen yang lebih terkelola dan terstruktur.
Melengkapi proyek Anda dengan menggunakan library gin, gin-helmet, xss-mw, dan cors. Berikut adalah langkah-langkahnya:
Langkah 1: Install Library helmet, xss-mw, dan cors
Pertama, kita menginstal library helmet, xss-mw, dan cors menggunakan perintah go get.
go get -u github.com/gin-contrib/helmet
go get -u github.com/utrack/gin-csrf
go get -u github.com/oxtoacart/bpool
Langkah 2: Lengkapi main.go dengan Middleware yang Dibutuhkan
1. Membuat Endpoint dan Handler
type Product struct {
Name string `json:"name"`
Price int `json:"price"`
Stock int `json:"stock"`
}
var products []Product
Di sini, kami mendefinisikan struktur Product dan sebuah variabel products untuk menyimpan produk.
2. Menyediakan Middleware untuk Melindungi dari Serangan XSS
Middleware ini mengambil handler HTTP berikutnya sebagai parameter, dan memodifikasi http.ResponseWriter dan http.Request sebelum meneruskannya ke handler berikutnya. Fungsi ini digunakan untuk melindungi aplikasi dari serangan XSS dengan membatasi ukuran payload body yang diterima.
Middleware ini mengatur header CORS pada setiap respons HTTP. Jika metode permintaan adalah OPTIONS, server akan merespons dengan status 204 No Content untuk menanggapi preflight request.
Middleware helmet digunakan untuk meningkatkan keamanan dengan menetapkan beberapa header keamanan pada setiap respons HTTP. Di sini, helmet.Default() mengembalikan handler yang diberi konfigurasi helmet default, dan kita mendaftarkan semua route pada default serve mux.