GoFiber+AWS SAM本地API:OpenAI审核标记后代码仍执行问题
问题背景
使用GoFiber结合AWS API Gateway开发API,通过sam local start-api本地测试。API在执行OpenAI生成文本/图像的核心逻辑前,会调用ModeratePromptInput函数检查用户输入。当前问题是:当审核函数理应返回错误时,调用处的err != nil判断无法终止后续代码执行,程序仍会继续走核心逻辑流程。
调用处关键代码
// 函数签名: // func GenerateOutput(c *fiber.Ctx) (interface{}, error) // .... // 创建OpenAI客户端用于依赖注入 oClient := openai.NewClient(os.Getenv("OPENAI_SECRET_KEY")) // 审核输入以防止滥用(若输入违规则提前退出) err := actions.ModeratePromptInput(c, oClient) if err != nil { log.Printf("Error moderating prompt input: %v", err) return nil, c.JSON(fiber.Map{ "error": "Error within error logic", }) } // 此处代码仍会执行 - 希望出错时终止
审核函数代码
func ModeratePromptInput(c *fiber.Ctx, oClient *openai.Client) error { promptInput := models.NewOutputRequest{} if err := c.BodyParser(&promptInput); err != nil { return err } moderationInput := openai.ModerationRequest{} moderationInput.Input = promptInput.UserInput res, err := oClient.Moderations(c.Context(), moderationInput) if err != nil || res.Results == nil || len(res.Results) == 0 { return c.Status(500).JSON(fiber.Map{ "error": "Could not moderate input", }) } if res.Results[0].Flagged { return c.Status(403).JSON(fiber.Map{ "error": "Input is abusive", "results:": res.Results, }) } return nil }
问题根源
核心原因是Fiber的c.Status().JSON()方法返回值为nil:
该方法的作用是向客户端发送HTTP响应,其返回的error类型值仅在响应发送失败时才会是非nil,成功发送响应时始终返回nil。这就导致你在ModeratePromptInput中返回c.Status(500).JSON(...)或c.Status(403).JSON(...)时,调用处拿到的err实际是nil,自然不会进入错误分支,后续代码会继续执行。
修复方案
方案1:分离错误逻辑与响应发送(推荐)
让审核函数仅负责业务逻辑判断,返回具体错误信息,由调用处统一处理响应发送,保持职责单一。
步骤1:定义自定义错误类型
在models包中定义用于传递审核错误的自定义类型:
package models import "github.com/sashabaranov/go-openai" type ModerationError struct { Message string Results []openai.ModerationResult StatusCode int } func (e *ModerationError) Error() string { return e.Message }
步骤2:修改审核函数
去掉函数内的响应发送逻辑,返回自定义错误或标准错误:
func ModeratePromptInput(c *fiber.Ctx, oClient *openai.Client) error { promptInput := models.NewOutputRequest{} if err := c.BodyParser(&promptInput); err != nil { return &models.ModerationError{ Message: "Failed to parse input", StatusCode: 400, } } moderationInput := openai.ModerationRequest{} moderationInput.Input = promptInput.UserInput res, err := oClient.Moderations(c.Context(), moderationInput) if err != nil || res.Results == nil || len(res.Results) == 0 { return &models.ModerationError{ Message: "Could not moderate input", StatusCode: 500, } } if res.Results[0].Flagged { return &models.ModerationError{ Message: "Input is abusive", Results: res.Results, StatusCode: 403, } } return nil }
步骤3:修改调用处逻辑
通过错误类型断言处理不同的错误场景:
err := actions.ModeratePromptInput(c, oClient) if err != nil { log.Printf("Error moderating prompt input: %v", err) var modErr *models.ModerationError if errors.As(err, &modErr) { // 处理审核相关错误,发送对应响应 response := fiber.Map{"error": modErr.Message} if modErr.Results != nil { response["results"] = modErr.Results } return nil, c.Status(modErr.StatusCode).JSON(response) } // 处理其他未知错误 return nil, c.Status(500).JSON(fiber.Map{ "error": err.Error(), }) } // 后续核心逻辑仅在无错误时执行
方案2:简化的哨兵错误方案
如果不想定义自定义错误,可以在审核函数中发送响应后,返回一个非nil的哨兵错误,确保调用处能进入错误分支:
步骤1:定义哨兵错误
var ErrModerationFailed = errors.New("moderation process failed")
步骤2:修改审核函数
发送响应后返回哨兵错误:
func ModeratePromptInput(c *fiber.Ctx, oClient *openai.Client) error { promptInput := models.NewOutputRequest{} if err := c.BodyParser(&promptInput); err != nil { c.Status(400).JSON(fiber.Map{"error": "Failed to parse input"}) return ErrModerationFailed } moderationInput := openai.ModerationRequest{} moderationInput.Input = promptInput.UserInput res, err := oClient.Moderations(c.Context(), moderationInput) if err != nil || res.Results == nil || len(res.Results) == 0 { c.Status(500).JSON(fiber.Map{"error": "Could not moderate input"}) return ErrModerationFailed } if res.Results[0].Flagged { c.Status(403).JSON(fiber.Map{ "error": "Input is abusive", "results": res.Results, }) return ErrModerationFailed } return nil }
步骤3:修改调用处逻辑
因为审核函数已经发送了响应,调用处只需直接返回即可:
err := actions.ModeratePromptInput(c, oClient) if err != nil { log.Printf("Error moderating prompt input: %v", err) // 响应已在审核函数中发送,直接返回终止流程 return nil, nil } // 后续核心逻辑仅在无错误时执行
总结
问题本质是对Fiber响应方法的返回值理解偏差,解决核心是确保审核函数在需要终止流程时,返回非nil的错误值,让调用处的err != nil判断生效。方案1更符合Go的错误处理最佳实践,便于后续扩展;方案2则更简洁,适合简单场景。
内容的提问来源于stack exchange,提问作者Joel Hager

