pemrograman

03 Mempelajari Route Pattern

Penggunaan Named Parameter

Apakah Router Pattern itu memiliki Pola? Betul sekali setiap endpoint itu memiliki pola parameter pada suatu URL dan sering disebut dengan Named Parameter. Named Parameter ini adalah pola pembuatan parameter dengan menggunakan nama. Setiap nama parameter diawali dengan titik dua : lalu diikuti dengan nama parameter-nya. Misalkan contoh seperti dibawah ini.

Pattern/user/:user
/user/santeknomatch
/user/kamumatch
/user/santekno/profilenot match
/user/not match

Kita akan mencoba mengimplementasikan bagaimana pada Named Parameter ini dibuat pada Golang. Pertama kita perlu buat terlebih dahulu fungsi handler seperti dibawah ini.

func NamedParameterHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	text := "Product " + p.ByName("id") + " Item " + p.ByName("itemId")
	fmt.Fprint(w, text)
}

Selanjutnya, buat router method GET pada file main.go untuk menginisialisasi endpoint Named Parameter yang kita buat seperti dibawah ini.

router.GET("/product/:id/items/:itemId", NamedParameterHandler)

Lalu jalankan program dan lakukan pengetesan dengan menggunakan curl.

curl --location --request GET 'localhost:8080/product/1/items/2'

Maka akan muncul hasil seperti dibawah ini.

➜  santekno-hugo git:(main) curl --location --request GET 'localhost:8080/product/1/items/2'
Product 1 Item 2% 

Selain testing manual kita pastikan juga membuat unit test dari handler yang sudah kita buat.

func TestNamedParameterHandler(t *testing.T) {
	type args struct {
		id     string
		itemId string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "test get params with product 1",
			args: args{
				id:     "1",
				itemId: "2",
			},
			want: "Product 1 Item 2",
		},
		{
			name: "test get params with product 2",
			args: args{
				id:     "2",
				itemId: "3",
			},
			want: "Product 2 Item 3",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			request := httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost/product/%s/item/%s", tt.args.id, tt.args.itemId), nil)
			recorder := httptest.NewRecorder()
			NamedParameterHandler(recorder, request, httprouter.Params{
				{
					Key:   "id",
					Value: tt.args.id,
				},
				{
					Key:   "itemId",
					Value: tt.args.itemId,
				},
			})

			response := recorder.Result()
			body, _ := io.ReadAll(response.Body)
			bodyString := string(body)

			assert.Equal(t, tt.want, bodyString)
		})
	}
}

Penggunaan Catch All Parameters

Selain Named Parameter, route pattern juga ada yang namanya Catch All Parameters yaitu menangkap semua parameter yang mana biasanya diawali dengan bintang *, lalu diikuti dengan nama parameter dan harus berada pada posisi terakhir URL. Lebih jelasnya berikut contoh pattern dibawah ini.

Pattern/src/*filepath
/user/not match
/user/namafilematch
/user/subdirektori/namafilematch

Cara kita mengimplementasikan Catch All Parameters sama halnya dengan Named Parameter tetapi pembedanya yaitu pada tanda, jika Named Parameter menggunakan tanda titik dua : sedangkan pada Catch All Parameters itu menggunakan tanda bintang *.

Baiklah kita coba buat fungsi handler terlebih dahulu.

func CatchAllParameterHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	text := "Image " + p.ByName("image")
	fmt.Fprint(w, text)
}

Lalu kita tambahkan router dengan method GET

router.GET("/images/*image", CatchAllParameterHandler)

Jalankan program dan kita coba lakukan pengetesan dengan menggunakan curl ini.

curl --location --request GET 'localhost:8080/images/small/image.jpg'

Setelah dijalankan maka akan terlihat keluaran dan mencetak seperti ini.

➜  santekno-hugo git:(main) ✗ curl --location --request GET 'localhost:8080/images/small/image.jpg'
Image /small/image.jpg%

Kita tambahkan juga unit test pada handler yang sudah kita buat agar memastikan kode kita benar-benar sesuai dengan ekspektasi kita.

func TestCatchAllParameterHandler(t *testing.T) {
	type args struct {
		image string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "get image",
			args: args{
				image: "photo.jpg",
			},
			want: "Image photo.jpg",
		},
		{
			name: "get image with path",
			args: args{
				image: "small/photo.jpg",
			},
			want: "Image small/photo.jpg",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			request := httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost/images/%s", tt.args.image), nil)
			recorder := httptest.NewRecorder()
			CatchAllParameterHandler(recorder, request, httprouter.Params{
				{
					Key:   "image",
					Value: tt.args.image,
				},
			})

			response := recorder.Result()
			body, _ := io.ReadAll(response.Body)
			bodyString := string(body)

			assert.Equal(t, tt.want, bodyString)
		})
	}
}
comments powered by Disqus