More articles -> ISLAND

As described in the previous chapter, it is easy to set up a simple Gin Web project and introduce some new concepts, such as routing the Router.

Routing is a very important concept. All interfaces must have routes to manage.

Request method

Gin routing supports GET, POST, PUT, DELETE, PATCH, HEAD, and OPTIONS requests, as well as an Any function that supports all of the above.

Add routes to other requests from the code in the previous chapter and write unit tests.

// omit other code
	// Add a Get request route
	router.GET("/".func(context *gin.Context) {
		context.String(http.StatusOK, "hello gin get method")})// Add a Post request route
	router.POST("/".func(context *gin.Context) {
		context.String(http.StatusOK, "hello gin post method")})// Add a Put request route
	router.PUT("/".func(context *gin.Context) {
		context.String(http.StatusOK, "hello gin put method")})// Add the Delete request route
	router.DELETE("/".func(context *gin.Context) {
		context.String(http.StatusOK, "hello gin delete method")})// Add a Patch request route
	router.PATCH("/".func(context *gin.Context) {
		context.String(http.StatusOK, "hello gin patch method")})// Add a Head request route
	router.HEAD("/".func(context *gin.Context) {
		context.String(http.StatusOK, "hello gin head method")})// Add Options request route
	router.OPTIONS("/".func(context *gin.Context) {
		context.String(http.StatusOK, "hello gin options method")})// omit other code
Copy the code

Unit test, show only a Post request function, basically the same as Get request, other code see Github address at the end of the article.

// Router ("/") Post test
func TestIndexPostRouter(t *testing.T) {
	router := initRouter.SetupRouter()
	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodPost, "/".nil)
	router.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "hello gin post method", w.Body.String())
}
Copy the code

At this point, you run the unit tests, and all the tests pass flawlessly.

There is a problem, however, that the routing function for all requests is basically the same, only slightly different, but we have written it all out in each route, so we need to extract the common logic.

func retHelloGinAndMethod(context *gin.Context) {
	context.String(http.StatusOK, "hello gin "+strings.ToLower(context.Request.Method)+" method")}Copy the code

We extract the common part of the Method and use context.request.method to extract the requested Method and convert it to lowercase. At this point we can modify our route, remove the function from the original route and replace it with the new function we wrote.

	// Add a Get request route
	router.GET("/", retHelloGinAndMethod)
	// Add a Post request route
	router.POST("/", retHelloGinAndMethod)
	// Add a Put request route
	router.PUT("/", retHelloGinAndMethod)
	// Add the Delete request route
	router.DELETE("/", retHelloGinAndMethod)
	// Add a Patch request route
	router.PATCH("/", retHelloGinAndMethod)
	// Add a Head request route
	router.HEAD("/", retHelloGinAndMethod)
	// Add Options request route
	router.OPTIONS("/", retHelloGinAndMethod)
Copy the code

Run the unit test at this point and still pass with flying flags.

The Handler processor

After the simple example above, we can now see that the route takes two arguments, one is the path, and the other is the method that the route executes, which we call Handler. Moreover, this parameter is variable length. That is, multiple handlers can be passed to form a handler chain.

There are also some requirements for the handler function, which needs to pass in a Gin.Context pointer, through which the handler should be processed.

The Handler function can return strings, Json, Html, and other files to the front end, as we’ll cover in detail.

Gets the parameters in the routing path

Now that you know the methods the route supports and the corresponding handlers, you should know how to get parameters from the route.

Write a new route as follows:

// omit other code
    / / add user
	router.GET("/user/:name",handler.Save)
// omit other code
Copy the code

At this point, we find that the/is present when there was only the/delimiter: this symbol indicates that the following string is a placeholder for the value to be passed. , our route is /user/{name}

We don’t need to put all of our handlers in one folder, that would be too bloated, so we create a new folder called Handler, create a userHandler.go file in that folder and write that file.

package handler

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func UserSave(context *gin.Context) {
	username := context.Param("name")
	context.String(http.StatusOK, "User has saved")}Copy the code

Similarly, we use context.Param to get the parameters in the routing path.

At this point, we can write our unit tests.

Create a new user_test.go file.

func TestUserSave(t *testing.T) {
	username := "lisi"
	router := initRouter.SetupRouter()
	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodGet, "/user/"+username, nil)
	router.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "User"+username+"Already saved", w.Body.String())
}
Copy the code

Run the unit tests and the tests pass. We can also run our project and type localhost:8080/user/lisi in the browser and we can also see that the user lisi has been saved on the browser page

Of course, there’s more than one way to get parameters. Gin provides different methods for obtaining parameters for different routes, such as: /user? Name = lisi&age = 18.

Let’s add a Handler again for processing. Add the following method to userHandler.

// Use the query method to get parameters
func UserSaveByQuery(context *gin.Context) {
	username := context.Query("name")
	age := context.Query("age")
	context.String(http.StatusOK, "Users."+username+",年龄:"+age+"Already saved")}Copy the code

At the same time to add and improve the routing.

	router.GET("/user", handler.UserSaveByQuery)
Copy the code

Once the routing is complete, the unit tests can be rewritten to improve the project.

func TestUserSaveQuery(t *testing.T) {
	username := "lisi"
	age := 18
	router := initRouter.SetupRouter()
	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodGet, "/user? name="+username+"&age="+strconv.Itoa(age), nil)
	router.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "Users."+username+",年龄:"+strconv.Itoa(age)+"Already saved", w.Body.String())
}
Copy the code

Run the test and the test passes. Localhost :8080/user? Name =lisi, page printed user :lisi, age :18 has been saved.

Of course, you can also use the context.DefaultQuery method, which assigns a default value if it is not available when fetching.

Re-modify the code to get the age to the following code

	age := context.DefaultQuery("age"."20")
Copy the code

Rewrite our unit tests and run them.

func TestUserSaveWithNotAge(t *testing.T) {
	username := "lisi"
	router := initRouter.SetupRouter()
	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodGet, "/user? name="+username, nil)
	router.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "Users."+username+", age :20 already saved", w.Body.String())
}
Copy the code

You can also access /user? From a browser. Name =lisi you can see that the browser shows that user :lisi, age :20 has been saved.

Of course, there are other methods for getting parameters, QueryArray for an array and QueryMap for a map.

Routing group

When we look at the SetupRouter method again, the routes in it can be basically divided into two categories: / and /user. If the function is added in the future, there will be a large number of routes, so we need to manage the routes. Gin provides us with routing groups.

Write the/route together and run the index_test.go unit test.

	index := router.Group("/")
	{
		// Add a Get request route
		index.GET("", retHelloGinAndMethod)
		// Add a Post request route
		index.POST("", retHelloGinAndMethod)
		// Add a Put request route
		index.PUT("", retHelloGinAndMethod)
		// Add the Delete request route
		index.DELETE("", retHelloGinAndMethod)
		// Add a Patch request route
		index.PATCH("", retHelloGinAndMethod)
		// Add a Head request route
		index.HEAD("", retHelloGinAndMethod)
		// Add Options request route
		index.OPTIONS("", retHelloGinAndMethod)
	}
Copy the code

Router. Group returns a new packet route, which is used to modify the original route. Of course groups can still be nested within groups.

In the request method, we said that there is an Any function that can pass Any request, so we can replace all requests in index with Any

	index := router.Group("/")
	{
		index.Any("", retHelloGinAndMethod)
	}
Copy the code

Run the unit tests, and they all pass.

At this time, I also found the benefits of unit test. Although it took time and experience to write the unit test before, in the future functional modification, we only need to run the unit test to know whether our function is correct, which greatly reduces the experience and time in the later functional test.

We also group users. Grouping not only puts the same logical code together, but also provides the same route prefix. The route after modification is still the same as before. Run the unit test user_test.go, and the unit test passes completely, proving that our code has no problems.

	userRouter := router.Group("/user")
	{
		userRouter.GET("/:name", handler.UserSave)
		userRouter.GET("", handler.UserSaveByQuery)
	}
Copy the code

conclusion

Through the use of simple routing, I have basically understood the position of routing in Gin, and also had some intuitive understanding of some common ways of using routing.

Code for this section

Github