HTTP-клиент получает код состояния 200, когда сервер паникует с помощью gin-gonic и gin-contrib / gzip

#go #go-gin

#Вперед #go-gin

Вопрос:

При обращении gin-gonic к нижеприведенному серверу HTTP-клиент должен получить код 500, но получает код 200.

 package main

import (
    "github.com/gin-contrib/gzip"
    "github.com/gin-gonic/gin"
)

func main() {
    gin.SetMode(gin.ReleaseMode)
    r := gin.New()
    r.Use(gin.Logger())
    r.Use(gin.Recovery())
    r.Use(gzip.Gzip(gzip.DefaultCompression))

    r.POST("/test", func(c *gin.Context) {
        panic("test")                        // Server panic and client should receive code 500.
    })

    r.Run(":8080")
}
  

При доступе /test с HTTP-клиента журнал go server выглядит так, как показано ниже, и возвращает код 500.

 [GIN] 2020/09/28 - 10:23:14 | 500 |     67.2995ms |             ::1 | POST     "/test"

2020/09/28 10:23:14 [Recovery] 2020/09/28 - 10:23:14 panic recovered:
test
C:/path/to/myproject/main.go:16 (0x8f193f)
    main.func1: panic("test")
  

Но HTTP-клиент получает код 200.

введите описание изображения здесь

Когда я удаляю r.Use(gzip.Gzip(gzip.DefaultCompression)) , HTTP-клиент получает код 500.

Почему клиент получает код 200 с r.Use(gzip.Gzip(gzip.DefaultCompression)) , как я могу это исправить?

Комментарии:

1. Я не смог воспроизвести это. Получение кода 500 с промежуточным программным обеспечением gzip и без него.

2. @LLawliet какую версию вы используете? Я использую go 1.14.6, gin 1.6.3, gin-contrib / gzip 0.0.3

3. go 1.15.2, gin 1.6.3, gin-contrib / gzip 0.0.3

4. Я попробовал перейти на 1.15.2, но все равно получает код 200.

5. Как вы выполняете POST? Вероятно, я виноват в использовании curl без заголовков accept.

Ответ №1:

Я воспроизвел ваш случай. Postman получил код 200, но сервер выдает 500 вместо этого.

Сервер вызовет c.Next() для выполнения 4 обработчиков при получении post-запросов. Последовательность действий следующая:

 gin.Logger
gin.Recovery
gzip.Gzip(gzip.DefaultCompression)
your handler
  

Вот gin ResponseWriter записывает заголовок ответа, и он будет записывать заголовок только один раз.

 func (w *responseWriter) WriteHeaderNow() {
    if !w.Written() {
        w.size = 0
        w.ResponseWriter.WriteHeader(w.status)
    }
}
  

Оба gzip.Gzip(gzip.DefaultCompression) и gin.Recovery имеют функцию отсрочки для записи заголовка ответа. Отложенные вызовы Golang выполняются в порядке поступления первыми. Поэтому gzip.Gzip(gzip.DefaultCompression) заголовок ответа будет записан в 200, а gin.Recovery заголовок ответа не будет записан в 500, как ожидалось.

Поэтому, чтобы решить эту проблему, вы должны изменить порядок обработчиков и убедиться gin.Recovery , что загружается последний обработчик.

Ответ №2:

Последнее добавление промежуточного программного обеспечения для восстановления, похоже, исправляет это.

 package main

import (
    "github.com/gin-contrib/gzip"
    "github.com/gin-gonic/gin"
)

func main() {
    gin.SetMode(gin.ReleaseMode)
    r := gin.New()
    r.Use(gin.Logger())
    r.Use(gzip.Gzip(gzip.DefaultCompression))
    r.Use(gin.Recovery())

    r.POST("/test", func(c *gin.Context) {
        panic("test")                        // Server panic and client should receive code 500.
    })

    r.Run(":8080")
}