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.
func NamedParameterHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
text := "Product " + p.ByName("id") + " Item " + p.ByName("itemId")
fmt.Fprint(w, text)
}
Next, create a GET
router method in the main.go
file to initialize the Named Parameter endpoint that we created as below.
router.GET("/product/:id/items/:itemId", NamedParameterHandler)
Then run the program and do a test using curl.
curl --location --request GET 'localhost:8080/product/1/items/2'
Then the results will appear as below.
➜ santekno-hugo git:(main) curl --location --request GET 'localhost:8080/product/1/items/2'
Product 1 Item 2%
Apart from manual testing, we make sure we also make unit tests from the handlers that we have created.
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)
})
}
}
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.
func CatchAllParameterHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
text := "Image " + p.ByName("image")
fmt.Fprint(w, text)
}
Then we add a router with the GET
method
router.GET("/images/*image", CatchAllParameterHandler)
Run the program and we try to do a test using this curl.
curl --location --request GET 'localhost:8080/images/small/image.jpg'
After running it, you will see output and print something like this.
➜ santekno-hugo git:(main) ✗ curl --location --request GET 'localhost:8080/images/small/image.jpg'
Image /small/image.jpg%
We also add unit tests to the handler that we have created to ensure that our code really meets our expectations.
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)
})
}
}