Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I intercept the response? #3582

Open
BlackHole1 opened this issue Apr 26, 2023 · 4 comments
Open

How do I intercept the response? #3582

BlackHole1 opened this issue Apr 26, 2023 · 4 comments

Comments

@BlackHole1
Copy link

Description

Could I intercept the response before it's sent? Similar to "PreSendResponse". This is a common requirement as there may be a possibility of failure after calling the middleware's c.Next() function, and I need to rewrite the Status Code. However, the request may have already been sent, and I cannot rewrite it. Here's a sample pseudocode that uses the DynamoDB database and executes a transaction commit after calling c.Next(). Once the transaction commit fails, I need to rewrite the Status Code and Response Body.

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

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

	r.GET("/ping", middleware(), func(c *gin.Context) {
		d := c.MustGet("dynamoDBService").(*dynamoDBService)
		d.Append(1 /* write request */)

		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	r.Run()
}

func middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		dynamoDBService := &dynamoDBService{}
		c.Set("dynamoDBService", dynamoDBService)

		c.Next()

		if err := dynamoDBService.Commit(); err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
		}
	}
}

type dynamoDBService struct {
	items []any
}

func (d *dynamoDBService) Commit() error {
	// return dynamodb.Client().Execute(d.items)
	return nil
}

func (d *dynamoDBService) Append(item any) {
	items = append(items, item)
}

The following is the code I wrote a simulation of this demand:

package main

import (
	"errors"
	"net/http"

	"github.com/gin-gonic/gin"
)

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

	r.GET("/", middleware(), func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	r.Run()
}

func middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()

		// Mock error
		c.AbortWithError(http.StatusInternalServerError, errors.New("error"))
	}
}

Expectations

$ curl -i http://localhost:8080                                                                         
HTTP/1.1 500 Internal Server Error
Date: Wed, 26 Apr 2023 02:01:10 GMT
Content-Length: 0

Actual result

$ curl -i http://localhost:8080                                                                         
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 26 Apr 2023 01:59:52 GMT
Content-Length: 18

{"message":"pong"}

Environment

  • go version: go1.20.3
  • gin version (or commit ref): v1.9.0
  • operating system: macOS
@BlackHole1
Copy link
Author

I found that when calling time.Sleep(3 * time.Second) after calling c.Next(), the final response is delayed by 3 seconds before being sent. Therefore, in Gin, the response is always sent after all middleware has executed, regardless of when the c.JSON method is called. However, it seems strange that there is no way to rewrite the response during this period.

func middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()

                fmt.Println("Response is not sent. Although it is not sent, you cannot modify the status code.")

		time.Sleep(3 * time.Second)

		fmt.Println("response sended!")
	}
}

@asbjornu
Copy link

This seems tangentially related to #3576.

@BlackHole1
Copy link
Author

@asbjornu It may be related. In my issue, once the status code is set, it cannot be set again forever. I personally feel that this is a very unreasonable situation.

@asbjornu
Copy link

asbjornu commented May 5, 2023

@BlackHole1 you not being able to set the status code seems related to me not being able to set Content-Type, as explained in gin-gonic/examples#106 (comment). I wonder what's going on here.

Also, I don't quite remember where I read it or the details of why it was, but I seem to recall that changing c.MustGet() with c.ShouldGet() may give you more flexibility to alter the response. Have you tried that? I'm not using c.MustGet() so it doesn't help me, but it may be worth a shot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants