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
Comment From: BlackHole1
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!")
}
}
Comment From: asbjornu
This seems tangentially related to #3576.
Comment From: BlackHole1
@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.
Comment From: asbjornu
@BlackHole1 you not being able to set the status code seems related to me not being able to set Content-Type
, as explained in https://github.com/gin-gonic/examples/issues/106#issuecomment-1530360137. 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.