Golang
  • Daftar isi
  • Pengenalan Golang
  • Cara instalasi Golang
  • Cara menjalankan Golang
  • Cara mendeklarasikan variabel di Golang
  • Apa saja tipe-tipe data di Golang
  • Cara Konversi Tipe Data di Golang
  • Apa saja operator-operator di Golang
  • Cara melakukan seleksi kondisi di Golang
  • Cara melakukan perulangan di Golang
  • Cara membuat Array di Golang
  • Cara membuat Slice di Golang
  • Cara membuat Slice dengan Operator Slice
  • Cara membuat Map di Golang
  • Cara membuat Pointer di Golang
  • Cara membuat Function di Golang
  • Cara membuat Package di Golang
  • Cara membuat multi return Function di Golang
  • Cara membuat Struct di Golang
  • Cara membuat method di Golang
  • Cara membuat Defer dan Scope di Golang
  • Cara membuat underscore(blank) di Golang
  • Cara membuat Goroutine di Golang
  • Cara membuat Channels di Golang
  • Cara membuat WaitGroup di Golang
  • Cara membuat Select statement di Golang
  • Cara membuat Http Server di Golang
  • Cara membuat Http Server di Golang part 2
  • Cara membuat Http Server di Golang part 3
  • Cara membuat Http Server di Golang part 4
  • Pengenalan Testing di Go
  • Membuat Unit Test di Go
Powered by GitBook
On this page

Cara membuat Http Server di Golang part 2

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:

  1. GetProductsHandler: Menghandle permintaan GET untuk mendapatkan daftar produk. Handler ini hanya mengizinkan metode GET.

  2. CreateProductHandler: Menghandle permintaan POST untuk membuat produk baru. Produk dibaca dari badan permintaan dan divalidasi sebelum disimpan dalam daftar produk.

  3. UpdateProductHandler: Menghandle permintaan PUT untuk memperbarui produk. Produk yang diperbarui dibaca dari badan permintaan dan dicari dalam daftar produk. Jika ditemukan, produk tersebut diperbarui.

  4. 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:

project-folder/
├── go.mod
├── main.go
└── src
    └── Controllers
        └── products.go

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:

main.go:

package main

import (
	"net/http"
	"project-folder/src/Controllers"
)

func main() {
	http.HandleFunc("/product", Controllers.GetProductsHandler)
	http.HandleFunc("/product/create", Controllers.CreateProductHandler)
	http.HandleFunc("/product/update", Controllers.UpdateProductHandler)
	http.HandleFunc("/product/delete", Controllers.DeleteProductHandler)
	http.ListenAndServe(":8080", nil)
}

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

func xssmw(next http.Handler) http.HandlerFunc {
    pool := bpool.NewBufferPool(64)

    return func(w http.ResponseWriter, r *http.Request) {
        if r.Method == "POST" || r.Method == "PUT" {
            buf := pool.Get()
            defer pool.Put(buf)

            buf.ReadFrom(r.Body)
            r.Body.Close()

            r.Body = http.MaxBytesReader(w, buf, int64(len(buf.Bytes())))
            w = &bodyWriter{body: buf, ResponseWriter: w}
        }
        next.ServeHTTP(w, r)
    }
}

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.

3. Menyediakan Middleware untuk Menangani CORS

func cors(next http.Handler) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Credentials", "true")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")

        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusNoContent)
            return
        }

        next.ServeHTTP(w, r)
    }
}

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.

4. Menggunakan Middleware Helmet

http.Handle("/", helmet.Default()(http.DefaultServeMux))

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.

5. Menggunakan Middleware XSS Protection dan CORS

http.Handle("/", http.HandlerFunc(xssmw(http.DefaultServeMux)))
http.Handle("/", http.HandlerFunc(cors(http.DefaultServeMux)))

Kami menggabungkan middleware xssmw dan cors dengan http.DefaultServeMux untuk melindungi aplikasi dari serangan XSS dan menangani CORS secara global.

PreviousCara membuat Http Server di GolangNextCara membuat Http Server di Golang part 3

Last updated 1 year ago