Golang中如何处理不可处理实体错误
Golang中如何处理不可处理实体错误
我在Go Fiber中创建了一个用于创建提示的POST API。从前端传递的载荷中,creator字段是字符串类型,但在我的prompt_model中,它被定义为primitive.ObjectId类型。当我运行函数c.BodyParser(&prompt)时,会抛出错误“bad request Unprocessable Entity”。
以下是Prompt模型:
type Prompts struct{
Id primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
Creator primitive.ObjectID `json:"creator,omitempty" bson:"creator,omitempty"`
CreatorUser User `json:"creatorUser,omitempty" bson:"-"`
Prompt string `json:"prompt,omitempty" validate:"required"`
Tag string `json:"tag,omitempty" validate:"required"`
}
以下是创建提示控制器的代码:
func CreatePrompt(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var prompt models.Prompts
// 从URL参数中提取会话ID
// sessionID := c.FormValue("creator")
// sessionObjectID, _ := primitive.ObjectIDFromHex(sessionID)
// fmt.Printf("ss %v \n ",sessionID)
// 验证请求体
fmt.Println("dd", c.BodyParser(&prompt))
if err := c.BodyParser(&prompt); err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{
Status: http.StatusBadRequest,
Message: "error",
Data: &fiber.Map{"data": err.Error()},
})
}
// 假设会话ID存储在Creator字段中
newPrompt := models.Prompts{
Id: primitive.NewObjectID(),
Creator: prompt.Creator,
Prompt: prompt.Prompt,
Tag: prompt.Tag,
}
result, err := promptCollection.InsertOne(ctx, newPrompt)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{
Status: http.StatusInternalServerError,
Message: "error",
Data: &fiber.Map{"data": err.Error()},
})
}
return c.Status(http.StatusCreated).JSON(responses.UserResponse{
Status: http.StatusCreated,
Message: "success",
Data: &fiber.Map{"data": result},
})
}
以下是来自前端的API调用:
const response = await fetch("http://localhost:8000/create-prompt", {
method: "POST",
body: JSON.stringify({
creator: session?.user.id,
prompt: post.prompt,
tag: post.tag,
}),
以下是载荷数据的日志:
{
"creator":"64ea57ad19b714af26ef3756",
"prompt":"ads",
"tag":"as"
}
我尝试在使用bodyParser方法之前将creator从字符串类型转换为objectId类型,但没有成功。我到底做错了什么?
更多关于Golang中如何处理不可处理实体错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢,它确实运行良好,你真是我的救星。
嗨,它起作用了,但我仍然有一个小问题。

如图所示,我希望 creator 字段能像第一部分那样,填入来自用户集合的用户ID以引用用户对象。但如您在第二个文档中所见,我并没有得到 creator 字段。
我尝试复现了这个问题,以下是我的发现。
- 请检查以下代码,确认我使用了正确的包且没有破坏原有逻辑:
package main
import (
"fmt"
fiber "github.com/gofiber/fiber/v2"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Prompts struct {
Id primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
Creator primitive.ObjectID `json:"creator,omitempty" bson:"creator,omitempty"`
Prompt string `json:"prompt,omitempty" validate:"required"`
Tag string `json:"tag,omitempty" validate:"required"`
}
func CreatePrompt(c *fiber.Ctx) error {
var prompt Prompts
// Validate the request body
fmt.Println("dd", c.BodyParser(&prompt))
if err := c.BodyParser(&prompt); err != nil {
return err
}
// Assuming the session ID is stored in the Creator field
newPrompt := Prompts{
Id: primitive.NewObjectID(),
Creator: prompt.Creator,
Prompt: prompt.Prompt,
Tag: prompt.Tag,
}
fmt.Printf("%#v\n", newPrompt)
return nil
}
func main() {
f := fiber.New()
f.Post("/test", CreatePrompt)
f.Listen(":8080")
}
- 如果代码没问题,我运行了这个应用并尝试使用 Postman 发送一个 POST 请求,请求体如下:

得到的响应是同样的错误:Unprocessable Entity。
- 在调试了
c.BodyParser函数后,我发现了可能的问题:解析器实际上不支持请求体的类型 “text/plain”。但是,当我在 Postman 请求中将请求体类型改为 “application/json” 后,它就如预期般正常工作,没有报错。我的建议是,在你的前端调用中明确指定内容类型,添加contentType: 'application/json',。
问题在于你的模型定义中Creator字段是primitive.ObjectID类型,但前端传递的是字符串类型的十六进制值。BodyParser无法自动将字符串转换为ObjectID类型。
以下是解决方案:
方法1:使用中间结构体接收字符串,然后转换
type CreatePromptRequest struct {
Creator string `json:"creator" validate:"required"`
Prompt string `json:"prompt" validate:"required"`
Tag string `json:"tag" validate:"required"`
}
func CreatePrompt(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var req CreatePromptRequest
// 解析请求体到中间结构体
if err := c.BodyParser(&req); err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{
Status: http.StatusBadRequest,
Message: "error",
Data: &fiber.Map{"data": err.Error()},
})
}
// 将字符串转换为ObjectID
creatorID, err := primitive.ObjectIDFromHex(req.Creator)
if err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{
Status: http.StatusBadRequest,
Message: "error",
Data: &fiber.Map{"data": "invalid creator ID format"},
})
}
// 创建新的Prompt对象
newPrompt := models.Prompts{
Id: primitive.NewObjectID(),
Creator: creatorID,
Prompt: req.Prompt,
Tag: req.Tag,
}
result, err := promptCollection.InsertOne(ctx, newPrompt)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{
Status: http.StatusInternalServerError,
Message: "error",
Data: &fiber.Map{"data": err.Error()},
})
}
return c.Status(http.StatusCreated).JSON(responses.UserResponse{
Status: http.StatusCreated,
Message: "success",
Data: &fiber.Map{"data": result},
})
}
方法2:为ObjectID实现自定义JSON反序列化
type JSONObjectID primitive.ObjectID
func (id *JSONObjectID) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
objectID, err := primitive.ObjectIDFromHex(s)
if err != nil {
return err
}
*id = JSONObjectID(objectID)
return nil
}
func (id JSONObjectID) MarshalJSON() ([]byte, error) {
return json.Marshal(primitive.ObjectID(id).Hex())
}
// 更新你的模型
type Prompts struct {
Id primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
Creator JSONObjectID `json:"creator,omitempty" bson:"creator,omitempty"`
CreatorUser User `json:"creatorUser,omitempty" bson:"-"`
Prompt string `json:"prompt,omitempty" validate:"required"`
Tag string `json:"tag,omitempty" validate:"required"`
}
// 使用时需要类型转换
newPrompt := models.Prompts{
Id: primitive.NewObjectID(),
Creator: models.JSONObjectID(creatorID),
Prompt: prompt.Prompt,
Tag: prompt.Tag,
}
方法3:使用自定义BodyParser(推荐)
type CreatePromptRequest struct {
Creator primitive.ObjectID `json:"creator" validate:"required"`
Prompt string `json:"prompt" validate:"required"`
Tag string `json:"tag" validate:"required"`
}
func (r *CreatePromptRequest) UnmarshalJSON(data []byte) error {
type Alias CreatePromptRequest
aux := &struct {
Creator string `json:"creator"`
*Alias
}{
Alias: (*Alias)(r),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
if aux.Creator != "" {
objectID, err := primitive.ObjectIDFromHex(aux.Creator)
if err != nil {
return fmt.Errorf("invalid creator ID: %v", err)
}
r.Creator = objectID
}
return nil
}
func CreatePrompt(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var req CreatePromptRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{
Status: http.StatusBadRequest,
Message: "error",
Data: &fiber.Map{"data": err.Error()},
})
}
newPrompt := models.Prompts{
Id: primitive.NewObjectID(),
Creator: req.Creator,
Prompt: req.Prompt,
Tag: req.Tag,
}
result, err := promptCollection.InsertOne(ctx, newPrompt)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{
Status: http.StatusInternalServerError,
Message: "error",
Data: &fiber.Map{"data": err.Error()},
})
}
return c.Status(http.StatusCreated).JSON(responses.UserResponse{
Status: http.StatusCreated,
Message: "success",
Data: &fiber.Map{"data": result},
})
}
方法1是最直接简单的解决方案,推荐使用。它清晰地分离了请求解析和类型转换,便于错误处理和调试。

