SoFunction
Updated on 2025-03-05

A brief analysis of the necessity of the existence of the Gin framework in Golang

1. Introduction

In Go,net/httpPackages provide a powerful and flexible standard HTTP library that can be used to build web applications and handle HTTP requests. This package is part of the Go language standard library, so all Go programs can use it directly. Since there is alreadynet/httpWhy does such a powerful and flexible standard library still appear likeGinThis makes it convenient for us to build third-party libraries for web applications?

Actually, it'snet/http, which provides basic HTTP functionality, but its design goal is to simplify and versatile, rather than provide advanced features and convenient development experiences. When handling HTTP requests and building web applications, you may encounter a series of problems, which also createsGinThe emergence of such a third-party library.

Below we will introduce a series of scenarios, through comparisonnet/httpandGinThe different implementations of the two in these scenarios will further illustrateGinThe necessity of the existence of the framework.

2. Complex routing scenario processing

In actual web application development, the scenario of using the same route prefix is ​​very common. Here are two more common examples.

For example, when designing APIs, the API may be updated and improved over time. To maintain backward compatibility and to allow multiple API versions to coexist, a similar/v1/v2Such routing prefixes are used to distinguish different versions of APIs.

There is another scenario where a large web application is often composed of multiple modules, each module responsible for different functions. In order to better organize the code and distinguish the routing of different modules, module names are often used as routing prefixes.

In both scenarios, it is likely that the same routing prefix will be used. If usingnet/httpTo frame web applications, the implementation is roughly as follows:

package main
import (
        "fmt"
        "net/http"
)
func handleUsersV1(w , r *) {
        (w, "User list in v1")
}
func handlePostsV1(w , r *) {
        (w, "Post list in v1")
}
func main() {
        ("/v1/users", handleUsersV1)
        ("/v1/posts", handlePostsV1)
        (":8080", nil)
}

In the example above, we use it manuallyto define different routing processing functions.

The code example doesn't seem to be a big problem, but because there are only two routing groups, if the number of handlers increases, the number of processing functions will also increase, and the code will become more complex and verbose. And each routing rule needs to manually set the routing prefix, as in the examplev1prefix, if the prefix is/v1/v2/...Setting up this way will lead to unclear code structure, complicated operations, and easy to make mistakes.

But by comparison,GinThe framework implements the function of routing packets, let's take a look at itGinFramework to implement this function:

package main
import (
        "fmt"
        "/gin-gonic/gin"
)
func main() {
        router := ()
        // Create a routing group        v1 := ("/v1")
        {
                ("/users", func(c *) {
                        (200, "User list in v1")
                })
                ("/posts", func(c *) {
                        (200, "Post list in v1")
                })
        }
        (":8080")
}

In the above example, byCreated av1When we set routing rules for routing prefixes, we do not need to set routing prefixes, and the framework will automatically assemble them for us.

At the same time, rules with the same route prefix are also maintained in the same code block. Compared withnet/httpCodebase,GinMake the code structure clearer and easier to manage.

3. Middleware processing

In the process of web application request processing, in addition to executing specific business logic, some common logic is often required before this, such as authentication operations, error processing or log printing functions. These logics are collectively called middleware processing logic, and are often indispensable.

First of all, for error handling, some internal errors may occur during the execution of the application, such as database connection failure, file reading errors, etc. Reasonable error handling can prevent these errors from crashing the entire application, but instead inform the client with appropriate error responses.

For authentication operations, in many web processing scenarios, it is often only after user authentication that they can access certain restricted resources or perform certain operations. At the same time, authentication operations can also limit user permissions to avoid unauthorized access by users, which helps improve the security of the program.

Therefore, a complete HTTP request processing logic is very likely to require these middleware processing logic. And theoretically, frameworks or class libraries should have support for middleware logic. Let's take a look at it firstnet/httpHow to implement it:

package main
import (
        "fmt"
        "log"
        "net/http"
)
// Error handling middlewarefunc errorHandler(next )  {
        return (func(w , r *) {
                defer func() {
                        if err := recover(); err != nil {
                                (w, "Internal Server Error", )
                                ("Panic: %v", err)
                        }
                }()
                (w, r)
        })
}
// Authentication and authentication middlewarefunc authMiddleware(next )  {
        return (func(w , r *) {
                // Simulated authentication                if ("Authorization") != "secret" {
                        (w, "Unauthorized", )
                        return
                }
                (w, r)
        })
}
// Handle business logicfunc helloHandler(w , r *) {
        (w, "Hello, World!")
}
// in additionfunc anotherHandler(w , r *) {
        (w, "Another endpoint")
}
func main() {
        // Create a router        router := ()
        // Application middleware, register processor        handler := errorHandler(authMiddleware((helloHandler)))
        ("/", handler)
        // Application middleware, register another request processor        another := errorHandler(authMiddleware((anotherHandler)))
        ("/another", another)
        // Start the server        (":8080", router)
}

In the above example, wenet/httpPassederrorHandlerandauthMiddlewareThe two middlewares implement error handling and authentication functions. Next, we look at line 49 of the sample code and we can find that the code adds error handling and authentication operation functions to the original processor through the decorator mode.

The advantage of the implementation of this code is that multiple processing functions are combined through the decorator mode to form a processor chain, which realizes error handling and authentication and authentication functions. Without needing to handle every functionhandlerAdding this part of the logic into it makes the code more readable and maintainable.

But there is also a very obvious disadvantage here, this function andNot a framework for usIt is achieved by ourselves. We add a new processing functionhandler, all need to do thishandlerDecorate and add error handling and authentication operations to it, which increases the burden on us and is also prone to errors. At the same time, the requirements are constantly changing. It is possible that some requests only require error processing, some requests only require authentication operations, and some requests require both error processing and authentication operations. Based on this code structure, it will become increasingly difficult to maintain.

By comparison,GinThe framework provides a more flexible way to enable and disable middleware logic, which can be set for a certain routing group without having to set individually for each routing rule. The following example code is shown below:

package main
import (
        "/gin-gonic/gin"
)
func authMiddleware()  {
        return func(c *) {
                // Simulated authentication                if ("Authorization") != "secret" {
                        (401, {"error": "Unauthorized"})
                        return
                }
                ()
        }
}
func main() {
        router := ()
        // Add Logger and Recovery middleware globally        // Create a routing group where all routes in the group will apply authMiddleware middleware        authenticated := ("/")
        (authMiddleware())
        {
                ("/hello", func(c *) {
                        (200, "Hello, World!")
                })
                ("/private", func(c *) {
                        (200, "Private data")
                })
        }
        // Not in the routing group, so no authMiddleware middleware is applied        ("/welcome", func(c *) {
                (200, "Welcome!")
        })
        (":8080")
}

In the above example, we("/")Created a name calledauthenticatedand then use the routing groupUseMethod, enable the routing groupauthMiddlewaremiddleware. All routing rules under this routing group will be automatically executedauthMiddlewareImplemented authentication operation.

Relative tonet/httpThe advantage of this is that it does not need to be for eachhandlerDecorate and add middleware logic. Users only need to focus on the development of business logic, reducing the burden.

Secondly, the maintenance is higher. If the business needs no longer need to perform authentication operations,ginJust delete itUsemethod call, andnet/httpIt needs to be allhandlerThe decorative operation is processed, and the authentication operation node in the decorator node is deleted, which is relative to the workload ofginVery large and easy to make mistakes.

at last,ginIn scenarios where different middleware is required to handle requests for different parts, it is more flexible and simpler to implement. For example, some requests require authentication operations, some requests require error processing, and some require both error processing and authentication operations. In this scenario, you only need to passginCreate three routing groupsrouter, and then different routing groups are called separatelyUseThe method enables different middleware to realize the requirements, which is relative tonet/httpMore flexible and maintainable.

This is why there isnet/httpOn the premise ofginOne of the important reasons for the framework.

4. Data binding

When processing HTTP requests, a common function is to automatically bind the data in the request to the structure. The following is a form data as an example, if usingnet/http, how to bind data to a structure:

package main
import (
        "fmt"
        "log"
        "net/http"
)
type User struct {
        Name  string `json:"name"`
        Email string `json:"email"`
}
func handleFormSubmit(w , r *) {
        var user User
        // Bind form data to User structure         = ("name")
         = ("email")
        // Process user data        (w, "User created:%s (%s)", , )
}
func main() {
        ("/createUser", handleFormSubmit)
        (":8080", nil)
}

We need to callFormValueMethod, you have to read out the data from the form one by one and then set it into the structure. Moreover, when there are many fields, we are likely to miss some of them, resulting in problems with the subsequent processing logic. Moreover, we need to manually read the settings for each field, which also affects our development efficiency.

Let's take a look belowGinHow to read form data and set it into the structure:

package main
import (
        "fmt"
        "/gin-gonic/gin"
)
type User struct {
        Name  string `json:"name"`
        Email string `json:"email"`
}
func handleFormSubmit(c *) {
        var user User
        // Bind form data to User structure        err := (&user)
        if err != nil {
                (, {"error": "Invalid form data"})
                return
        }
        // Process user data        (, {"message": ("User created:%s (%s)", , )})
}
func main() {
        router := ()
        ("/createUser", handleFormSubmit)
        (":8080")
}

Look at line 17 of the above sample code and you can see the direct callShouldBindFunctions can automatically map the form data to the structure automatically, no longer need to read fields one by one, and then set them to the structure separately.

Compared to usingnet/http, ginFramework is more convenient in terms of data binding, and it is not prone to errors.ginVariousapi, Can map various types of data into structures, users only need to call the correspondingapiJust do it. andnet/httpThe corresponding operation is not provided, and the user needs to read the data and then manually set it into the structure.

5. Summary

In Go,net/httpBasic HTTP functionality is provided, but it is designed to be simple and versatile, rather than providing advanced features and convenient development experiences. When handling HTTP requests and building web applications, it will seem unscrupulous when handling complex routing rules; at the same time, it is difficult to achieve pluggable design for some public operations, such as logging, error processing, etc.; if you want to bind the request data to the structure,net/httpThere are no simple operations provided, they all need to be implemented manually by users.

This is why it appears likeGinSuch a third-party library is built onnet/httpOn top of it, it aims to simplify and accelerate the development of web applications.

Overall,GinIt can help developers build web applications more efficiently, providing a better development experience and richer functions. Of course, choose to usenet/httpOr Gin depends on the size, needs and personal preferences of the project. For simple small projects,net/httpIt may be enough, but for complex applications, Gin may be more suitable.

This is the end of this article about a brief analysis of the necessity of the Gin framework in Golang. For more related Golang Gin content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!