英文:
Run different validation depending on the HTTP Method of the request [Go Gin]
问题
所以我目前正在使用Go和Gin库开发一个简单的API,并希望在进一步检查之前对从客户端接收到的数据进行一些验证。
我已经了解到了标签的用法,并且以下是我目前的一个清晰示例:
type Model struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `json:"name" bson:"name" binding:"required"`
ProductId int `json:"productId" bson:"productId" binding:"required,min=1"`
Brand string `json:"brand" bson:"brand" binding:"required"`
Model string `json:"model" bson:"model" binding:"required"`
Weight string `json:"weight" bson:"weight"`
Observations string `json:"observations" bson:"observations"`
}
通过使用binding标签,我可以检查在收到POST请求时这些字段是否包含在请求体中。
现在我的问题是...如果,例如,我不想要求在PATCH请求中的所有这些字段怎么办?
用户可能只想更新观察结果或者只想更新名称,而不必提供所有其他的东西,因为它们已经保存在数据库中。
在这里,最好的方法是什么?编写一个使用HTTP请求方法的自定义验证器?创建不同的结构体来在不同的HTTP方法中使用?
非常感谢你的帮助。
英文:
So I'm currently developing a simple API using Go and the Gin library and wanted to have some validation of the data that I receive from the client before further checking.
I have come across the usage of tags and this is a clear example of what I have so far:
type Model struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `json:"name" bson:"name" binding:"required"`
ProductId int `json:"productId" bson:"productId" binding:"required,min=1"`
Brand string `json:"brand" bson:"brand" binding:"required"`
Model string `json:"model" bson:"model" binding:"required"`
Weight string `json:"weight" bson:"weight"`
Observations string `json:"observations" bson:"observations"`
}
Thanks to the binding tags I can check that these fields are included in the body when I get a POST request.
Now my question is... What happens if, for example, I don't want to require all of these fields for a PATCH request?
The user may want to update only the observations or only the Name without having to provide all of the rest of the things since they're already saved in the database.
What is the best approach here? Write a custom validator that uses the methods of the HTTP request? Create different structs to use in different HTTP methods?
Thank you so much for your help.
答案1
得分: 1
我认为在这里最好的模式是将HTTP接口与底层数据模型解耦。因此,在这种情况下,你应该为每个路由/方法组合使用单独的结构体,并在验证后将这些结构体编组成一个公共的模型类型。
英文:
I think best pattern here is to decouple the HTTP interface from the underlying data model. So in this case, you should have separate structs for each route/method combination, and marshal those structs into a common model type after validation.
答案2
得分: 0
你可以使用自定义的结构标签来替代"binding",并编写一个验证函数来检查这些标签。
type Model struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `json:"name" bson:"name" post:"required"`
ProductId int `json:"productId" bson:"productId" post:"required,min=1"`
Brand string `json:"brand" bson:"brand" post:"required"`
Model string `json:"model" bson:"model" post:"required"`
Weight string `json:"weight" bson:"weight"`
Observations string `json:"observations" bson:"observations"`
}
func validateStruct(data interface{}) error {
value := reflect.ValueOf(data)
if value.Kind() != reflect.Struct {
return fmt.Errorf("validateStruct: expected a struct, got %T", data)
}
for i := 0; i < value.NumField(); i++ {
field := value.Type().Field(i)
tag := field.Tag.Get("post")
// fmt.Println("Binding", field.Tag.Get("binding"))
if tag != "" {
rules := strings.Split(tag, ",")
for _, rule := range rules {
// fmt.Println(rule)
if rule == "required" {
fieldValue := value.Field(i).Interface()
zero := reflect.Zero(field.Type).Interface()
if reflect.DeepEqual(fieldValue, zero) {
return fmt.Errorf("%s is required", field.Name)
}
}
}
}
}
return nil
}
以上是你要翻译的内容。
英文:
You may use custom struct tags instead of "binding" and have a validator function that will check for those tags.
type Model struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `json:"name" bson:"name" post:"required"`
ProductId int `json:"productId" bson:"productId" post:"required,min=1"`
Brand string `json:"brand" bson:"brand" post:"required"`
Model string `json:"model" bson:"model" post:"required"`
Weight string `json:"weight" bson:"weight"`
Observations string `json:"observations" bson:"observations"`
}
func validateStruct(data interface{}) error {
value := reflect.ValueOf(data)
if value.Kind() != reflect.Struct {
return fmt.Errorf("validateStruct: expected a struct, got %T", data)
}
for i := 0; i < value.NumField(); i++ {
field := value.Type().Field(i)
tag := field.Tag.Get("post")
// fmt.Println("Binding", field.Tag.Get("binding"))
if tag != "" {
rules := strings.Split(tag, ",")
for _, rule := range rules {
// fmt.Println(rule)
if rule == "required" {
fieldValue := value.Field(i).Interface()
zero := reflect.Zero(field.Type).Interface()
if reflect.DeepEqual(fieldValue, zero) {
return fmt.Errorf("%s is required", field.Name)
}
}
}
}
}
return nil
}
答案3
得分: 0
个人而言,我可能会选择不同的结构体 - 创建一个基本模型,其中包含每种类型请求中都持久存在的字段,然后在特定的结构体中继承它,用于GET/PATCH等操作。
英文:
Personally, I'd most likely go for different structs - create a base model with fields persistent in every type of request, then inherit it in specific structs for GET/PATCH etc.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论