Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
03 Oct 2023 · 4 min read ·Article 62 / 119
Go

03 Learn About Route Pattern

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

Use of Named Parameter

The Pattern Router have a have a Pattern to handle http or web application for every endpoint has a parameter pattern in a URL and is often called a Named Parameter. Named Parameter is a pattern for creating parameters using names. Each parameter name begins with a colon : followed by the parameter name. For example, an example like the one below.

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

We will try to implement how these Named Parameter are created in Golang. First, we need to create a handler function as below.

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

Next, create a GET router method in the main.go file to initialize the Named Parameter endpoint that we created as below.

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

Then run the program and do a test using curl.

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

Then the results will appear as below.

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

Apart from manual testing, we make sure we also make unit tests from the handlers that we have created.

go
 1func TestNamedParameterHandler(t *testing.T) {
 2	type args struct {
 3		id     string
 4		itemId string
 5	}
 6	tests := []struct {
 7		name string
 8		args args
 9		want string
10	}{
11		{
12			name: "test get params with product 1",
13			args: args{
14				id:     "1",
15				itemId: "2",
16			},
17			want: "Product 1 Item 2",
18		},
19		{
20			name: "test get params with product 2",
21			args: args{
22				id:     "2",
23				itemId: "3",
24			},
25			want: "Product 2 Item 3",
26		},
27	}
28	for _, tt := range tests {
29		t.Run(tt.name, func(t *testing.T) {
30			request := httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost/product/%s/item/%s", tt.args.id, tt.args.itemId), nil)
31			recorder := httptest.NewRecorder()
32			NamedParameterHandler(recorder, request, httprouter.Params{
33				{
34					Key:   "id",
35					Value: tt.args.id,
36				},
37				{
38					Key:   "itemId",
39					Value: tt.args.itemId,
40				},
41			})
42
43			response := recorder.Result()
44			body, _ := io.ReadAll(response.Body)
45			bodyString := string(body)
46
47			assert.Equal(t, tt.want, bodyString)
48		})
49	}
50}

Use of Catch All Parameters

Apart from Named Parameter, the route pattern also has something called Catch All Parameters which is to catch all parameters which usually start with a star *, then followed by the name of the parameter and must be at the last position of the URL. For more details, here is an example of the pattern below.

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

The way we implement Catch All Parameters is the same as Named Parameter but the difference is in the sign, if Named Parameter uses a colon : whereas in Catch All Parameters it uses an asterisk *.

OK, let’s try creating a handler function first.

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

Then we add a router with the GET method

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

Run the program and we try to do a test using this curl.

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

After running it, you will see output and print something like this.

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

We also add unit tests to the handler that we have created to ensure that our code really meets our expectations.

go
 1func TestCatchAllParameterHandler(t *testing.T) {
 2	type args struct {
 3		image string
 4	}
 5	tests := []struct {
 6		name string
 7		args args
 8		want string
 9	}{
10		{
11			name: "get image",
12			args: args{
13				image: "photo.jpg",
14			},
15			want: "Image photo.jpg",
16		},
17		{
18			name: "get image with path",
19			args: args{
20				image: "small/photo.jpg",
21			},
22			want: "Image small/photo.jpg",
23		},
24	}
25	for _, tt := range tests {
26		t.Run(tt.name, func(t *testing.T) {
27			request := httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost/images/%s", tt.args.image), nil)
28			recorder := httptest.NewRecorder()
29			CatchAllParameterHandler(recorder, request, httprouter.Params{
30				{
31					Key:   "image",
32					Value: tt.args.image,
33				},
34			})
35
36			response := recorder.Result()
37			body, _ := io.ReadAll(response.Body)
38			bodyString := string(body)
39
40			assert.Equal(t, tt.want, bodyString)
41		})
42	}
43}

Related Articles

💬 Comments