Gin gonic框架:响应状态码令人困惑

huangapple go评论82阅读模式
英文:

Gin gonic framework: confusing response status codes

问题

我已经进入了一个我不理解的领域。

这是一个使用Golang编写的Gin API路由处理程序的示例。


// src/file1.go

func CreateDataModelAction(c *gin.Context) {
    var input inputs.CreateDataModelInput

    if err := c.ShouldBindJSON(&input); err != nil {
        // 这里我使用一个生成并返回验证错误的API响应的函数。
        // 这个函数位于单独的文件中,下面将列出该代码块。
        api.RespondWithValidationError(c, outputs.GetValidationErrors(err))
        return
    }

    c.JSON(http.StatusCreated, nil)
}

// src/file2.go

func RespondWithValidationError(c *gin.Context, validationErrors outputs.DataValidationErrorAPIResponse) {
    c.AbortWithStatusJSON(http.StatusUnprocessableEntity, validationErrors)
}

ShouldBindJSON之后发生验证错误的情况下,我期望这段代码进入if块,并返回带有验证错误的422状态码。然而,无论如何,这段代码始终返回状态码200,尽管这个函数只能返回422201

但是,如果我删除api.RespondWithValidationError函数,并直接在路由处理程序中使用AbortWithStatusJSON函数,我会收到预期的422状态码。


// 这个示例按预期工作

func CreateDataModelAction(c *gin.Context) {
    var input inputs.CreateDataModelInput

    if err := c.ShouldBindJSON(&input); err != nil {
        c.AbortWithStatusJSON(http.StatusUnprocessableEntity)
        return
    }

    c.JSON(http.StatusCreated, nil)
}

我试图阅读Gin上下文源代码,以了解为什么会发生这种情况,但到目前为止我还没有成功。

请解释一下在这种情况下Gin上下文是如何工作的,以及为什么当我从单独的函数返回422状态码时,我没有收到422状态码。

英文:

I've stepped into something I don't understand.

Here is an example of Gin API route handler, written in Golang.


// src/file1.go

func CreateDataModelAction(c *gin.Context) {
    var input inputs.CreateDataModelInput

    if err := c.ShouldBindJSON(&input); err != nil {
        // Here I use a function that generates and returns API responses for validation errors.
        // This function is located in separate file and it's going to be listed below this code        block.
        api.RespondWithValidationError(c, outputs.GetValidationErrors(err))
	return
    }

    c.JSON(http.StatusCreated, nil)
}

// src/file2.go

func RespondWithValidationError(c *gin.Context, validationErrors outputs.DataValidationErrorAPIResponse) {
    c.AbortWithStatusJSON(http.StatusUnprocessableEntity, validationErrors)
}

In case of validation errors happening after ShouldBindJSON I expect this code to enter if block and to return 422 status code with validation errors. However, this code always returns status code 200 despite the fact thic function can only return 422 or 201.

But if I remove api.RespondWithValidationError function and use AbortWithStatusJSON function directly in route handler, I receive the status code 422 as it is expected.


// This example works as expected

func CreateDataModelAction(c *gin.Context) {
    var input inputs.CreateDataModelInput

    if err := c.ShouldBindJSON(&input); err != nil {
        c.AbortWithStatusJSON(http.StatusUnprocessableEntity)
	return
    }

    c.JSON(http.StatusCreated, nil)
}

I was trying to read Gin context source code in order to understand why this is happening, and I have not succeeded so far.

Please explain to me how exactly Gin context works in this case and why I'm not receiving 422 when I'm returning 422 status code from separate function.

答案1

得分: 1

我根据你的问题代码编写了一个小例子,并猜测了你省略的细节,但相关的代码应该是正确的。为了演示和更简洁,我将所有的代码都放在了一个文件中。

package main

import (
	"net/http"

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

// HTTP请求提供的输入DTO
type createDataModeInput struct {
	Name string `json:"name" binding:"required"`
}

// 发送错误场景下的输出DTO
type DataValidationErrorApiResponse struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

// 构建DataValidationErrorApiResponse实例的函数
func GetValidationErrors(err error) DataValidationErrorApiResponse {
	return DataValidationErrorApiResponse{
		Code:    "VALIDATION_ERR",
		Message: err.Error(),
	}
}

func RespondWithValidationError(c *gin.Context, validationErrors DataValidationErrorApiResponse) {
	c.AbortWithStatusJSON(http.StatusUnprocessableEntity, validationErrors)
}

func CreateDataModeAction(c *gin.Context) {
	var input createDataModeInput
	if err := c.ShouldBind(&input); err != nil {
		RespondWithValidationError(c, GetValidationErrors(err))
		return
	}
	c.JSON(http.StatusCreated, nil)
}

func main() {
	gin.SetMode(gin.DebugMode)
	r := gin.Default()
	r.POST("/demo", CreateDataModeAction)
	r.Run(":8000")
}

如你所见,代码与你的代码非常相似。现在,让我们看看如何正确测试它。

CURL测试

为了测试代码,我使用了两个CURL命令:一个必须返回422状态码,另一个返回201状态码。后一个命令如下:

curl -X POST http://127.0.0.1:8000/demo -H 'Content-Type: application/json' -d '{"name":"lorem"}'

通过这个命令,你将得到created - 201状态码。要测试错误场景,你可以使用类似以下的命令:

curl -X POST http://127.0.0.1:8000/demo -H 'Content-Type: application/json' -d '{"name":""}'

使用这个命令,你将得到期望的422状态码。

抱歉,如果我没有添加太多解释,但代码与你的代码非常相似。如果你仍然遇到问题,请告诉我,我会更新我的回答,谢谢!

英文:

I put together a small example based on the code of your question. I guessed the details you omitted but the relevant code should be good. I put all of the code in a single file just for the sake of the demo and to be more concise.

package main

import (
	"net/http"

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

// input DTO provided by HTTP Request
type createDataModeInput struct {
	Name string `json:"name" binding:"required"`
}

// output DTO to send in error scenario
type DataValidationErrorApiResponse struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

// function to build the DataValidationErrorApiResponse instance
func GetValidationErrors(err error) DataValidationErrorApiResponse {
	return DataValidationErrorApiResponse{
		Code:    "VALIDATION_ERR",
		Message: err.Error(),
	}
}

func RespondWithValidationError(c *gin.Context, validationErrors DataValidationErrorApiResponse) {
	c.AbortWithStatusJSON(http.StatusUnprocessableEntity, validationErrors)
}

func CreateDataModeAction(c *gin.Context) {
	var input createDataModeInput
	if err := c.ShouldBind(&input); err != nil {
		RespondWithValidationError(c, GetValidationErrors(err))
		return
	}
	c.JSON(http.StatusCreated, nil)
}

func main() {
	gin.SetMode(gin.DebugMode)
	r := gin.Default()
	r.POST("/demo", CreateDataModeAction)
	r.Run((":8000"))
}

As you can see, the code is pretty much similar to yours. Now, let's see how to properly test it.

CURL tests

To test out the code, I used two CURL commands: one that must be resolved with a 422 status code and one with 201. The latter command is:

curl -X POST http://127.0.0.1:8000/demo -H 'Content-Type: application/json' -d '{"name":"lorem"}'

Thanks to this, you'll get the created - 201 status code. To test the bad scenario, you should use something like:

curl -X POST http://127.0.0.1:8000/demo -H 'Content-Type: application/json' -d '{"name":""}'

With this statement, you'll get the desired 422 status code.
Sorry, if I didn't add too much explanation but the code is really similar to yours. If you still face some issues, just let me know and I'll update my answer, thanks!

huangapple
  • 本文由 发表于 2023年6月20日 15:24:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76512267.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定