03 Learn About Route Pattern
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/santekno | match |
| /user/kamu | match |
| /user/santekno/profile | not 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.
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.
1router.GET("/product/:id/items/:itemId", NamedParameterHandler)Then run the program and do a test using curl.
1curl --location --request GET 'localhost:8080/product/1/items/2'Then the results will appear as below.
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.
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/namafile | match |
| /user/subdirektori/namafile | match |
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.
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
1router.GET("/images/*image", CatchAllParameterHandler)Run the program and we try to do a test using this curl.
1curl --location --request GET 'localhost:8080/images/small/image.jpg'After running it, you will see output and print something like this.
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.
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}