2. Handling HTTP Requests with Gin – Generating HTTP Responses

2.5 Generating HTTP Responses

2.5.1 Setting the Response Body

In the Gin framework, you can use methods in <span>gin.Context</span> such as <span>String()</span>, <span>JSON()</span>, <span>XML()</span>, <span>HTML()</span>, and <span>File()</span> to generate HTTP responses. The <span>gin.Context</span> object is passed as a parameter to the route handler function, providing methods to set the <span>response status code</span>, <span>headers</span>, and <span>response body</span>.

  • <span>String(code int, format string, values ...any)</span>: Sets the response body to a formatted string, generating content using the provided format string and parameters, while setting the response’s Content-Type to <span>text/plain</span>
  • <span>JSON(code int, obj any)</span>: Sets the response body to JSON format, while setting the response’s Content-Type to <span>application/json</span>
  • <span>XML(code int, obj any)</span>: Sets the response body to XML format, while setting the response’s Content-Type to <span>application/xml</span>
  • <span>HTML(code int, name string, obj any)</span>: Renders the specified HTML template with the provided data, setting the result as the response body, while setting the response’s Content-Type to <span>text/html</span>
  • <span>File(filepath string)</span>: Sets the response body to the content of the specified file, automatically setting the response’s Content-Type based on the file’s extension

Through these various methods, you can easily generate different types of HTTP responses to meet various development requirements.

2.5.1.1 String/HTML Response

In Gin, if you want to respond to a request with a string, you can send text content (usually plain text or HTML) to the client.

2.5.1.1.1 String Response

The example is as follows:

package main

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

func main() {
    r := gin.Default()
    // Define route
    r.GET("/string", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        ctx.String(http.StatusOK, "Client IP accessed: %s", clientIP)
    })
    // Default listen on 0.0.0.0:8080
    r.Run()
}
2.5.1.1.2 HTML Response

If the response string contains HTML content, you can set the content type to text/html. The example code is as follows:

package main

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

func main() {
    r := gin.Default()
    // Use the first method to render HTML
    r.GET("/html-1", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        content := fmt.Sprintf("<h1>Client IP accessed: %s </h1>", clientIP)
        // Send response to client
        ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(content))
    })

    r.LoadHTMLGlob("templates/*")
    r.GET("/html-2", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        content := fmt.Sprintf("Client IP accessed: %s", clientIP)
        ctx.HTML(http.StatusOK, "index.tmpl", gin.H{
            "body": content,
        })
    })
    // Default listen on 0.0.0.0:8080
    r.Run()
}

The index.tmpl file is as follows:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Testing Gin HTML Response</title>
</head>
<body>
    <h1>{{.body}}</h1>
</body>
</html>

The <span>Data(code int, contentType string, data []byte)</span> method is used to send raw data, specifying the status code, content type, and content. If you need to use the <span>ctx.HTML()</span> method to send an HTML response, you need to load the template file in advance.

2.5.1.2 JSON Response

In Gin, responding to requests in JSON format is very common, especially in web and API scenarios. Typically, there are two ways to do this:

  • Using the <span>gin.Context</span> method <span>JSON()</span> to respond
  • Using a <span>struct</span> for <span>JSON()</span> response

The example is as follows:

package main

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

type ResponseData struct {
    CleintIP string `json:"client_ip"`
    Status   int    `json:"status_code"`
}

func main() {
    r := gin.Default()
    // Using gin.Context object for JSON response
    r.GET("/json-1", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        // Send response to client
        ctx.JSON(http.StatusOK, gin.H{
            "client_ip": clientIP,
        })
    })
    // Using struct for JSON response
    r.GET("/json-2", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        // Send response to client
        ctx.JSON(http.StatusOK, &ResponseData{
            CleintIP: clientIP,
            Status:   http.StatusOK,
        })
    })
    // Default listen on 0.0.0.0:8080
    r.Run()
}

2.5.1.3 XML Response

In Gin, responding with XML is similar to responding with JSON, typically also including two forms of response.

  • Using the <span>gin.Context</span> method <span>XML()</span> to respond
  • Using a <span>struct</span> for <span>XML()</span> response

The example is as follows:

package main

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

type ResponseData struct {
    CleintIP string `xml:"client_ip"`
    Status   int    `xml:"status_code"`
}

func main() {
    r := gin.Default()
    // Using gin.Context object for XML response
    r.GET("/xml-1", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        // Send response to client
        ctx.XML(http.StatusOK, gin.H{
            "client_ip":   clientIP,
            "status_code": http.StatusBadGateway,
        })
    })
    // Using struct for XML response
    r.GET("/xml-2", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        // Send response to client
        ctx.XML(http.StatusOK, &ResponseData{
            CleintIP: clientIP,
            Status:   http.StatusOK,
        })
    })
    // Default listen on 0.0.0.0:8080
    r.Run()
}

The final response result is as follows:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<map>
<status_code>502</status_code>
<client_ip>127.0.0.1</client_ip>
</map>

2.5.2 Setting Response Headers

In the Gin framework, you can use the <span>gin.Context</span> object method <span>Header()</span> to set HTTP response headers. This can be divided into setting a single response header, setting multiple response headers, and setting redirect response headers. The example code is as follows:

package main

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

func main() {
    r := gin.Default()

    r.GET("/header-1", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        // Set a single response header
        ctx.Header("X-Customer-Header", "Single-Header-Customer")
        // Send response to client
        ctx.JSON(http.StatusOK, gin.H{
            "client_ip":       clientIP,
            "status_code":     http.StatusBadGateway,
            "customer_header": ctx.Writer.Header(),
        })
    })

    r.GET("/header-2", func(ctx *gin.Context) {
        clientIP := ctx.ClientIP()
        // Set multiple response headers
        ctx.Header("X-Customer-Header-1", "Multi-Header-Customer-1")
        ctx.Header("X-Customer-Header-2", "Multi-Header-Customer-2")
        // Send response to client
        ctx.JSON(http.StatusOK, gin.H{
            "client_ip":       clientIP,
            "status_code":     http.StatusOK,
            "customer_header": ctx.Writer.Header(),
        })
    })

    r.GET("/header-3", func(ctx *gin.Context) {
        // Set redirect response header
        ctx.Header("Cache-Control", "no-cache, no-store, must-revalidate")
        // Send response to client and specify redirect URL
        ctx.Redirect(http.StatusTemporaryRedirect, "https://www.surpassme.net/")
    })

    // Default listen on 0.0.0.0:8080
    r.Run()
}

The running results are as follows:

// Single Header
{
    "client_ip": "127.0.0.1",
    "customer_header": {
        "Content-Type": [
            "application/json; charset=utf-8"
        ],
        "X-Customer-Header": [
            "Single-Header-Customer"
        ]
    },
    "status_code": 502
}
// Multiple Header
{
    "client_ip": "127.0.0.1",
    "customer_header": {
        "Content-Type": [
            "application/json; charset=utf-8"
        ],
        "X-Customer-Header-1": [
            "Multi-Header-Customer-1"
        ],
        "X-Customer-Header-2": [
            "Multi-Header-Customer-2"
        ]
    },
    "status_code": 200
}

Leave a Comment