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.
package main
import (
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
router := httprouter.New()
router.GET("/", SmpleGetHandler)
router.POST("/", SamplePostHandler)
server := http.Server{
Handler: router,
Addr: "localhost:8080",
}
server.ListenAndServe()
}
The code above is the contents of the main.go
file. Then we create a new file handler.go
whose contents are as below.
package main
import (
"fmt"
"net/http"
"github.com/julienschmidt/httprouter"
)
func SampleGetHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
fmt.Fprint(w, "Hello Get")
}
func SamplePostHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
fmt.Fprint(w, "Hello Post")
}
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.
func GetUsedParamsHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
text := "Product " + p.ByName("id")
fmt.Fprint(w, text)
}
Then we call the handler function in the main function like this.
router.GET("/product/:id",GetUsedParamsHandler)
Run the project or program above then try using curl to access the endpoint that we have created like this.
➜ santekno-hugo git:(main) ✗ curl --location --request GET 'http://localhost:8080/product/1'
Product 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.
func TestGetUsedParamsHandler(t *testing.T) {
type args struct {
params string
}
tests := []struct {
name string
args args
want string
}{
{
name: "test get params with product 1",
args: args{
params: "1",
},
want: "Product 1",
},
{
name: "test get params with product 2",
args: args{
params: "2",
},
want: "Product 2",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
request := httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost/product/%s", tt.args.params), nil)
recorder := httptest.NewRecorder()
GetUsedParamsHandler(recorder, request, httprouter.Params{
{
Key: "id",
Value: tt.args.params,
},
})
response := recorder.Result()
body, _ := io.ReadAll(response.Body)
bodyString := string(body)
assert.Equal(t, tt.want, bodyString)
})
}
}
Then we will also add other unit tests to the functions we created previously like this.
func TestSampleGetHandler(t *testing.T) {
tests := []struct {
name string
want string
}{
{
name: "test get",
want: "Hello Get",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "http://localhost/", nil)
recorder := httptest.NewRecorder()
SampleGetHandler(recorder, request, nil)
response := recorder.Result()
body, _ := io.ReadAll(response.Body)
bodyString := string(body)
if !reflect.DeepEqual(bodyString, tt.want) {
t.Errorf("response = %v, want %v", bodyString, tt.want)
}
assert.Equal(t, tt.want, bodyString)
})
}
}
func TestSamplePostHandler(t *testing.T) {
tests := []struct {
name string
want string
}{
{
name: "test post",
want: "Hello Post",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
request := httptest.NewRequest(http.MethodPost, "http://localhost/", nil)
recorder := httptest.NewRecorder()
SamplePostHandler(recorder, request, nil)
response := recorder.Result()
body, _ := io.ReadAll(response.Body)
bodyString := string(body)
assert.Equal(t, tt.want, bodyString)
})
}
}