2.4 Handling HTTP Requests
The process of handling HTTP requests in Gin is mapping the request path to the corresponding handler function through routing. Each request first passes through middleware (if any), and then the routing handler function executes the corresponding business logic based on the request method and path. After processing, Gin returns the response to the client through the gin.Context object.
2.4.1 Getting GET Request Parameters
In Gin, GET request parameters can be obtained from the URL in several ways:
1. Query Parameters
When the client makes a GET request, it can attach query parameters in the URL. Gin provides the Query() method of the gin.Context object to retrieve parameters. If a parameter does not exist, the DefaultQuery() method can be used to set a default value for the parameter.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func queryUserInfo(ctx *gin.Context) {
// Get parameters from the request URL, use default values if parameters do not exist
name := ctx.DefaultQuery("name", "nil")
age := ctx.DefaultQuery("age", "18")
location := ctx.DefaultQuery("location", "China")
ctx.JSON(http.StatusOK, map[string]any{"name": name, "age": age, "location": location})
}
func main() {
r := gin.Default()
// Define route group
r.GET("/user", queryUserInfo)
// Default listen on 0.0.0.0:8080
r.Run()
}
2. Binding Query Parameters to Structs
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type person struct {
// Fields need to be capitalized to be exported
Name string `form:"name" binding:"required"`
Age string `form:"age"`
Location string `form:"location"`
}
func queryUserInfo(ctx *gin.Context) {
var p person
if err := ctx.ShouldBindQuery(&p); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, gin.H{"name": p.Name, "age": p.Age, "location": p.Location})
}
func main() {
r := gin.Default()
// Define route group
r.GET("/user", queryUserInfo)
// Default listen on 0.0.0.0:8080
r.Run()
}
In the above example, the parameters of the GET request are automatically bound to the person struct. If binding fails, a 400 error is returned.
2.4.2 Getting POST Request Parameters
In Gin, when handling POST request parameters, data needs to be extracted from the request body. Depending on the content type of the body (such as form data, JSON data, XML data, etc.), different methods can be used to handle POST data. The main features of Gin handling POST requests are as follows:
- Content Type Detection Gin automatically detects the Content-Type field in the request header and processes data based on the type.
- Processing Different Data Based on Content Type Depending on the content type, use different methods in the
<span>gin.Context</span>to handle the corresponding data.
2.4.2.1 Handling Form Data
If the request body contains form data, the <span>PostForm()</span> method of the gin.Context object can be used to get the values of form fields, as shown in the following example:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Handle form data
func handlePostFormData(ctx *gin.Context) {
name := ctx.PostForm("name")
age := ctx.PostForm("age")
ctx.JSON(http.StatusOK, gin.H{
"name": name,
"age": age,
})
}
func main() {
r := gin.Default()
// Define route group
r.POST("/user", handlePostFormData)
// Default listen on 0.0.0.0:8080
r.Run()
}
2.4.2.2 Handling JSON Data
If the request body contains JSON data, the <span>ShouldBindJSON()</span> method of the gin.Context object can be used to bind the JSON data to a struct, as shown in the following example:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Location string `json:"location"`
}
// Handle JSON data
func handlePostJsonData(ctx *gin.Context) {
var p Person
if err := ctx.ShouldBindJSON(&p); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"name": p.Name,
"age": p.Age,
"location": p.Location,
})
}
func main() {
r := gin.Default()
// Define route group
r.POST("/user", handlePostJsonData)
// Default listen on 0.0.0.0:8080
r.Run()
}
2.4.2.3 Handling XML Data
If the request body contains XML data, the <span>ShouldBindXML()</span> method of the gin.Context object can be used to bind the data to a struct, as shown in the following example:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Person struct {
Name string `xml:"name"`
Age int `xml:"age"`
Location string `xml:"location"`
}
// Handle XML data
func handlePostXmlData(ctx *gin.Context) {
var p Person
if err := ctx.ShouldBindXML(&p); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"name": p.Name,
"age": p.Age,
"location": p.Location,
})
}
func main() {
r := gin.Default()
// Define route group
r.POST("/user", handlePostXmlData)
// Default listen on 0.0.0.0:8080
r.Run()
}
2.4.2.4 Binding Request Data to Structs
Gin supports binding request data directly to structs, regardless of whether the data type is form, JSON, or XML. The <span>ShouldBind()</span> method of the gin.Context object can simplify the process of handling multiple parameters. The example code is as follows:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Person struct {
Name string `form:"name" json:"name" xml:"name" binding:"required"`
Age int `form:"age" json:"age" xml:"age" `
Location string `form:"location" json:"location" xml:"location" `
}
// Handle POST request data
func handlePostRequestData(ctx *gin.Context) {
var p Person
if err := ctx.ShouldBind(&p); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"name": p.Name,
"age": p.Age,
"location": p.Location,
})
}
func main() {
r := gin.Default()
// Define route group
r.POST("/user", handlePostRequestData)
// Default listen on 0.0.0.0:8080
r.Run()
}
2.4.2.5 Validating Struct Data
Gin supports validating fields of structs through struct tags. For example, you can specify which fields are required, define ranges for certain fields, etc. The example code is as follows:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Range validation reference: https://juejin.cn/post/6863765115456454664
type Person struct {
Name string `form:"name" json:"name" xml:"name" binding:"required"`
Age int `form:"age" json:"age" xml:"age" binding:"gt=0,lte=120"`
Location string `form:"location" json:"location" xml:"location" binding:"max=10,min=1"`
}
// Handle POST request data
func handlePostRequestData(ctx *gin.Context) {
var p Person
if err := ctx.ShouldBind(&p); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"name": p.Name,
"age": p.Age,
"location": p.Location,
})
}
func main() {
r := gin.Default()
// Define route group
r.POST("/user", handlePostRequestData)
// Default listen on 0.0.0.0:8080
r.Run()
}
2.4.3 Binding Request Parameters to Structs
In Gin, to bind request parameters to a struct, the incoming HTTP request data needs to be mapped to a Go struct object. This is particularly useful when handling form data and JSON data. The example is as follows:
- Tagging Structs
To allow Gin to correctly bind request parameters to structs, tags must be used to annotate the struct. These tags provide information on how to bind parameters. For example, use the form tag to specify the name of the form parameter.
type Person struct {
Name string `form:"name" json:"name" xml:"name" binding:"required"`
Age int `form:"age" json:"age" xml:"age" binding:"gt=0,lte=120"`
Location string `form:"location" json:"location" xml:"location" binding:"max=10,min=1"`
}
- Binding in Handler Functions
Using the <span>ShouldBind()</span> method in <span>gin.Context</span>, request data can be bound to a struct. This method automatically detects the data type of the request and performs the binding operation. The example code is as follows:
type Person struct {
Name string `form:"name" json:"name" xml:"name" binding:"required" `
Age int `form:"age" json:"age" xml:"age" binding:"gt=0,lte=120"`
Location string `form:"location" json:"location" xml:"location" binding:"max=10,min=1"`
}
// Handle POST request data
func handlePostRequestData(ctx *gin.Context) {
var p Person
if err := ctx.ShouldBind(&p); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"name": p.Name,
"age": p.Age,
"location": p.Location,
})
}
2.4.4 Practical Example
By accessing the corresponding interface address, the client’s IP address is returned. The example code is as follows:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// Define route
r.GET("/client_ip", func(ctx *gin.Context) {
clientIP := ctx.ClientIP()
ctx.JSON(http.StatusOK, gin.H{
"message": "Hello Surpass",
"clientIP": clientIP,
})
})
// Default listen on 0.0.0.0:8080
r.Run()
}
In the above code, the route <span>/client_ip</span> is defined, and in the handler function, the <span>ClientIP()</span> method is used to get the IP address of the client making the request, which is included in the returned JSON response.
In the above example, the ClientIP() method returns the client IP address in the following order:
- X-Forwarded-For header: If it exists and contains a non-internal IP address, return each IP address.
- X-Real-IP header: If the above condition is not met, but this header exists and contains a non-internal IP address, return its value.
- Remote address: If the above headers do not exist or are invalid, return the remote address of the TCP connection. If the program runs behind a reverse proxy or load balancer, ensure they are correctly configured to forward the real client IP address in the X-Forwarded-For or X-Real-IP headers.