在Golang中,部分更新REST API的正确/约定方式是什么?

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

What's the correct/agreed way to partial update REST API in Golang

问题

让我们假设我们有一个结构体:

type Person struct {
	ID                  string          `json:"id"`
	Name                string          `json:"name"`
	DOB                 string          `json:"dob"`
	Status              string          `json:"status"` // enabled/disabled
	Address             string          `json:"address"`
	DataRetentionPeriod uint32          `json:"dataRetentionPeriod"`
	PrimaryContact      `json:"primaryContact"`
}

type PrimaryContact struct {
	Name  string `json:"name"`
	Phone string `json:"phone"`
	Email string `json:"email"`
}

我们希望能够根据需要部分更新这个结构体。例如,我们只想更改他的AddressDataRetentionPeriodPrimaryContact。我们正在使用DynamoDb,因此在发送更新时,最终产品需要是map[string]interface{}

在Go中,最佳/正确的方法是什么?

以下是我们有的几种方法:

1) 每次发送一个新的完整主体。这样可以解决问题,但问题是客户端需要在网络上发送比必要数据更多的数据。因此,我们希望避免这种情况。

2) 我们可以将原始结构体转换为指针,然后使用reflect检查空值,然后将其转换为map[string]interface{}以发送到SDK。

3) 使用类似于structs的包来构建一个只包含所需字段的来自API的新结构体。

4) 将主体解组为map[string]json.RawMessage,然后映射到项目并验证正确/必需的字段,然后将其转换为map[string]interface{}

还有其他几种方法,但没有一种方法看起来很简洁。

请指导如何在Golang中使用部分更新进行REST API。

谢谢。

英文:

Let's say we have a struct

type Person struct {
	ID                          string `json:"id"`
	Name                        string `json:"name"`
	DOB                         string `json:"dob"` 
	Status                      string `json:"status"`     // enabled/disabled
	Address                     string `json:"address"`
	DataRetentionPeriod         uint32 `json:"dataRetentionPeriod"`
	PrimaryContact              `json:"primaryContact"`
}

type PrimaryContact struct {
	Name  string `json:"name"`
	Phone string `json:"phone"`
	Email string `json:"email"`
}

We want to be able to partial update this struct as we see fit. For example, we want to only change his Address or DataRetentionPeriod or PrimaryContact. We are using DynamoDb so the end product needs to be map[string]interface{} when sending the updates.

What is the best/correct way of doing this in Go?

These are the following approaches we have:

1) Send a new complete body every time. This will resolve the issue but the problem is the client needs to send more data than necessary over the network. So we want to avoid this.

2) We can turn the original struct into pointers and then check the nil values using reflect and then transform it into map[string] interface to send to the SDK

3) Use a package like structs to build a new struct that's coming from the API with only the fields required

4) Unmarshal the body into a map[string]json.RawMessage and then map over the items and verify the correct/required fields and then transform it into map[string]interface

A few others but none of them feel clean.

Please advise on "best" way to do partial updates using Golang for REST APIs.

Thank you.

答案1

得分: 1

有很多方法可以实现你提到的需求。

gRPC 使用了一种适用于我的用例的方法。基本上是使用 PATCH 请求,其中请求包含两个结构体:你的数据结构和一个更新掩码([]string),掩码指定了将要更新的内容。

考虑以下示例:

type Person struct {
    ID                          string `json:"id"`
    Name                        string `json:"name"`
    DOB                         string `json:"dob"` 
    Status                      string `json:"status"`     // enabled/disabled
    Address                     string `json:"address"`
    DataRetentionPeriod         uint32 `json:"dataRetentionPeriod"`
    PrimaryContact              `json:"primaryContact"`
}

type PrimaryContact struct {
    Name  string `json:"name"`
    Phone string `json:"phone"`
    Email string `json:"email"`
}

// PersonRequest 将通过 HTTP PATCH 方法在你的服务中接受
// 如果 `updateMask` 被填充,它将指定客户端所需的更改
// 一个请求的示例可能如下所示:
//
// myUpdateRequest := PersonRequest{
//     Person{Name: "joe", DOB: "01-01-1970", PrimaryContact: PrimaryContact{Phone: "+12223334444"}},
//     UpdateMask: []string{"name", "dob", "primaryContact.phone"},
// }
//
type PersonRequest struct {
    Person     Person   `json:"person"`
    UpdateMask []string `json:"updateMask"`
}

以上是代码的翻译部分。

英文:

There are many approaches as you noted.

gRPC uses an approach that works well for my use cases. Basically a PATCH where the request contains two structs your data struct and an update mask ([]string), the mask specifies what will be updated.

consider this

type Person struct {
    ID                          string `json:"id"`
    Name                        string `json:"name"`
    DOB                         string `json:"dob"` 
    Status                      string `json:"status"`     // enabled/disabled
    Address                     string `json:"address"`
    DataRetentionPeriod         uint32 `json:"dataRetentionPeriod"`
    PrimaryContact              `json:"primaryContact"`
}

type PrimaryContact struct {
    Name  string `json:"name"`
    Phone string `json:"phone"`
    Email string `json:"email"`
}

// PersonRequest would be accepted via http PATCH method in you service
// the `updateMask` if populated would specify the client's desired changes
// an example of Request might look like this
//
// myUpdateRequest := PersonRequest{
//     Person{Name:"joe",DOB:"01-01-1970",PrimaryContact: PrimaryContact{Phone: "+12223334444"}},
//     UpdateMask: []string{"name", "dob", "primaryContact.phone"},
// }
//
Type PersonRequest struct {
    Person     Person   `json:"person"`
    UpdateMask []string `json:"updateMask"`
}

</details>



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

发表评论

匿名网友

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

确定