02 Learning About HTTP Router Params
Use of HTTP Router Params
The httprouter.Handle has an additional parameter, namely Params, which is used to store parameters sent from the client, but this Params is not a query for parameters but is a parameter from the URL. Sometimes we need to create URLs that are not fixed or can change, for example /product/1, /product/2 and so on.
ServerMux does not support this, so on the Router there are additional parameters, one of which is to handle things like this. However, for the Router we need to add something to the Route so that the URL Path becomes dynamic.
How to Implement
Previously we created an easy sample handler and how to call a function so that it can run on the project. Next we will try to create an endpoint that uses a dynamic URL as explained above.
Before going there, we will first change our code so that it is neatly arranged, namely the endpoint that we created in the previous post. become like this.
1package main
2
3import (
4 "net/http"
5
6 "github.com/julienschmidt/httprouter"
7)
8
9func main() {
10 router := httprouter.New()
11
12 router.GET("/", SmpleGetHandler)
13 router.POST("/", SamplePostHandler)
14
15 server := http.Server{
16 Handler: router,
17 Addr: "localhost:8080",
18 }
19
20 server.ListenAndServe()
21}The code above is the contents of the main.go file. Then we create a new file handler.go whose contents are as below.
1package main
2
3import (
4 "fmt"
5 "net/http"
6
7 "github.com/julienschmidt/httprouter"
8)
9
10func SampleGetHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
11 fmt.Fprint(w, "Hello Get")
12}
13
14func SamplePostHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
15 fmt.Fprint(w, "Hello Post")
16}Once our code is neat, we will continue adding an endpoint that implements dynamic URL params. So, add the code below to the handler.go file.
1func GetUsedParamsHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
2 text := "Product " + p.ByName("id")
3 fmt.Fprint(w, text)
4}1router.GET("/product/:id",GetUsedParamsHandler)Run the project or program above then try using curl to access the endpoint that we have created like this.
1➜ santekno-hugo git:(main) ✗ curl --location --request GET 'http://localhost:8080/product/1'
2Product 1%If we change the parameter 1 then a display will appear that corresponds to the input, for example 2 then Product 2 will appear.
Added Unit Tests
After we create the handler params above, we will also try to ensure that our handler really meets the needs we want, namely by creating a unit test from the function we created earlier. The following is the unit test function for the code above.
1func TestGetUsedParamsHandler(t *testing.T) {
2 type args struct {
3 params string
4 }
5 tests := []struct {
6 name string
7 args args
8 want string
9 }{
10 {
11 name: "test get params with product 1",
12 args: args{
13 params: "1",
14 },
15 want: "Product 1",
16 },
17 {
18 name: "test get params with product 2",
19 args: args{
20 params: "2",
21 },
22 want: "Product 2",
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/product/%s", tt.args.params), nil)
28 recorder := httptest.NewRecorder()
29 GetUsedParamsHandler(recorder, request, httprouter.Params{
30 {
31 Key: "id",
32 Value: tt.args.params,
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} 1func TestSampleGetHandler(t *testing.T) {
2 tests := []struct {
3 name string
4 want string
5 }{
6 {
7 name: "test get",
8 want: "Hello Get",
9 },
10 }
11 for _, tt := range tests {
12 t.Run(tt.name, func(t *testing.T) {
13 request := httptest.NewRequest(http.MethodGet, "http://localhost/", nil)
14 recorder := httptest.NewRecorder()
15 SampleGetHandler(recorder, request, nil)
16
17 response := recorder.Result()
18 body, _ := io.ReadAll(response.Body)
19 bodyString := string(body)
20
21 if !reflect.DeepEqual(bodyString, tt.want) {
22 t.Errorf("response = %v, want %v", bodyString, tt.want)
23 }
24
25 assert.Equal(t, tt.want, bodyString)
26 })
27 }
28}
29
30func TestSamplePostHandler(t *testing.T) {
31 tests := []struct {
32 name string
33 want string
34 }{
35 {
36 name: "test post",
37 want: "Hello Post",
38 },
39 }
40 for _, tt := range tests {
41 t.Run(tt.name, func(t *testing.T) {
42 request := httptest.NewRequest(http.MethodPost, "http://localhost/", nil)
43 recorder := httptest.NewRecorder()
44 SamplePostHandler(recorder, request, nil)
45
46 response := recorder.Result()
47 body, _ := io.ReadAll(response.Body)
48 bodyString := string(body)
49
50 assert.Equal(t, tt.want, bodyString)
51 })
52 }
53}