通过部分 json 更新 REST API
Posted
技术标签:
【中文标题】通过部分 json 更新 REST API【英文标题】:REST API Update by partial json 【发布时间】:2019-07-13 16:50:19 【问题描述】:我们正在使用 Golang 实现一个包含 CRUD 的 REST API,在更新服务中,客户端可以发送包含更改字段的部分 JSON,我们需要处理这些更改来更新实体。
从逻辑上讲,我们需要通过 Id 从 DB 获取实体到 struct,然后将 payload json 解组到另一个 struct 并更新实体。
但是,如果有效载荷 json 不完整,例如我有 struct
type Customer struct
Id int64 `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
JSON 请求看起来像
"Name": "Updated name"
然后应该用新名称更新客户。
这是个简单的例子,实际上它可以是一个嵌套的struct和嵌套的json,我们如何用golang,或者Java,.NET等其他语言来处理这种情况
【问题讨论】:
您目前如何将 json 解组为 Go 结构? 你可以使用 json.RawMessage 参考这个答案:***.com/questions/11066946/… 如果您在已经填充的结构之上解组 JSON,则只有 JSON 中的字段会在结构中被修改,这非常容易 - 从数据库加载记录,在其上解组 JSON ,然后将其写回数据库。您能否展示您尝试过的方法以及遇到的问题? 请注意,通过RFC 7231,您要么需要使用PATCH
执行部分更新,要么通过PUT
更新与实际资源部分重叠的资源。对于实际资源,这具有部分更新的效果,尽管PUT
的语义仍然存在:用请求中提供的有效负载替换目标资源的当前有效负载。其他任何内容都违反了 HTTP 协议。此外,修补程序应向服务器发送有关如何修改资源以最终处于所需状态的说明。
您可能想做的最接近的事情是PATCH
ing 媒体类型为application/merge-patch+json
的资源,如RFC 7396 中所述,并且仅适用于此类媒体类型。我仍然建议使用application/json-patch+json
中指定的RFC 6902 虽然
【参考方案1】:
如果更新请求使用相同的 Customer
结构,则结构字段可以是用于区分零值和未在 JSON 中设置的值的指针。
现在您需要做的就是将现有结构合并到更新的Consumer
结构中。
为此,您可以在 Go 中使用 https://github.com/imdario/mergo 库。
package main
import (
"fmt"
"github.com/imdario/mergo"
"encoding/json"
"os"
)
type Address struct
City string `json:"city"`
type Customer struct
Id int64 `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Address *Address `json:"address"`
func main()
old1 := &CustomerId:1, Name:"alpha", Age:5, Address:&AddressCity:"Delhi"
b := []byte(`"name": "beta"`) //no address, age specified picks from old
up1 := new(Customer)
json.Unmarshal(b, up1)
if err := mergo.Merge(up1, old1); err != nil
fmt.Printf("err in 1st merge: %v\n", err)
os.Exit(1)
m1, _ := json.Marshal(up1)
fmt.Printf("merged to: %v\n", string(m1))
old2 := &CustomerId:1, Name:"alpha", Age:5, Address:&AddressCity:"Delhi"
b2 := []byte(` "address": "city": "mumbai"`) //address specified
up2 := new(Customer)
json.Unmarshal(b2, up2)
if err := mergo.Merge(up2, old2); err != nil
fmt.Printf("err in 1st merge: %v\n", err)
os.Exit(1)
m2, _ := json.Marshal(up2)
fmt.Printf("merged to: %v\n", string(m2))
【讨论】:
感谢@Saurav Prakash,但是如果我想将 Age 的值更新为 0,那么这个框架是不可能的【参考方案2】:从您的 cmets 看来,您遇到了很多 go
用户遇到的零值问题,即如何判断输入数据是否通过了合法值 - 或者该值是否被默认遗漏归零。
解决这个问题的唯一方法是使用指针。因此,在您的示例中,将您的数据结构更改为:
type Customer struct
Id *int64 `json:"id"`
Name *string `json:"name"`
Age *int `json:"age"`
然后在解组后,任何未初始化的字段都将具有 nil
值,例如
var c Customer
err := json.Unmarshal(jsonData, &c)
if err != nil
panic(err)
if c.Id != nil
log.Println("TODO: added SQL update parms for c.Id:", *c.Id)
if c.Name != nil
log.Println("TODO: added SQL update params for c.Name:", *c.Name)
if c.Age != nil
log.Println("TODO: added SQL update parms for c.Age:", *c.Age)
注意:必须注意确保不会意外引用任何会触发即时 panic
的 nil 指针。
工作playground example。
【讨论】:
以上是关于通过部分 json 更新 REST API的主要内容,如果未能解决你的问题,请参考以下文章
api rest 调用更新 django 模型时出现错误 415
将 jquery 与 django rest api 放在一起