【发布时间】:2023-02-21 19:23:15
【问题描述】:
我想使用现有的处理程序而不实际发送 http 请求(http.Do 或类似的东西),所以我使用 gin.CreateTestContext(httptest.NewRecorder()) 创建新的 *gin.Context 来调用现有的 Accept 处理程序。
我已经尝试使用 c.Copy() 来复制 AcceptOrders 上下文并使用它们来调用现有的 Accept 处理程序,但是当在现有的 Accept 处理程序上调用 c.JSON 时我会感到恐慌
我也尝试使用不带副本的 c 传递给现有的 Accept 处理程序,但我无法覆盖 c.JSON,因为它已经在现有的 Accept 处理程序上被调用
有没有更好的方法来实现我想要的(在另一个处理程序中调用现有处理程序)?
感谢您的答复
func (cms *CmsOrderHandler) AcceptOrders(c *gin.Context) {
var referenceIds constant.OrderReferenceIDs
err := c.Bind(&referenceIds)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "bad_request",
"message": err.Error(),
})
return
}
if len(referenceIds.ReferenceIds) == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"error": "bad_request",
"message": err.Error(),
})
return
}
for _, referenceId := range referenceIds.ReferenceIds {
r := httptest.NewRecorder()
gin.SetMode(gin.ReleaseMode)
cCopy, _ := gin.CreateTestContext(r)
req, err := http.NewRequest(http.MethodPost, "", nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "internal server error",
"message": err.Error(),
})
return
}
cCopy.Params = append(c.Params, gin.Param{Key: "reference_id", Value: referenceId})
cCopy.Request = req
cms.Accept(cCopy)
}
c.JSON(http.StatusOK, gin.H{})
}
【问题讨论】:
-
“在处理程序中调用处理程序”-- 对我来说听起来像个坏主意;为什么不将 Accept 处理程序的逻辑转换为不依赖于 gin 上下文的普通函数,然后从 Accept 和 AcceptOrders 调用该函数?
-
@mkopriva,我可以,但是我想避免很多变化,我能知道为什么这对你来说不是个好主意吗?
-
原则上这是个坏主意。如果您在为 API 构造参数时遇到问题,则说明您没有使用适当的 API。在生产代码中依赖测试包(httptest.NewRecorder、gin.CreateTestContext)是可疑的。这些东西不符合相同的标准;例如,他们经常恐慌而不是优雅地处理错误。此外, http.NewRequest 创建出站请求,而不是服务器请求。有一些细微的差异可能会在某个时候回来咬你。
-
“我能知道原因吗”-- 原因是它不能像您已经了解到的那样开箱即用。处理程序(和它们的中间件)旨在处理它们自己的请求,它们不应该在它们之间分工。这就是设计。你违背了设计。即使您尝试做的事情是可能的,它仍然是错误的。每当您不得不诉诸恶作剧时,这都是一个坏主意。使用正确的工具来完成工作。
-
感谢@mkopriva 的回复。我会按照您的建议将 Accept 处理程序逻辑转换为普通函数,并从 Accept 和 AcceptOrders 中调用它