使用Golang进行HTTP PATCH语义的REST更新请求

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

golang - Rest update request using http patch semantics

问题

type A struct {
Id int64
Email sql.NullString
Phone sql.NullString
}

假设我在数据库中有一条记录

A{1, "x@x.com", "1112223333"}

通过PUT发送更新请求

curl -X PUT -d '{"Email": "y@y.com", "Phone": null}' http://localhost:3000/a/1

这是一个伪算法,可以用于完整的PUT请求(即更新记录A的所有字段),但在PATCH请求语义上会引起困难。

  • 将JSON解组为空记录

    a := A{}
    json.Unmarshal([]byte(request.body), &a)

  • 从数据库加载记录

    aFromDb := <假设我们从数据库获取了记录> // A{1, "x@x.com", "1112223333"}

  • 比较a和aFromDb

  • 注意电子邮件的更改并在aFromDb上设置它 - 没问题

  • 注意电话号码的更改 - 但等等!在JSON中它是显式设置为NULL还是根本没有包含在JSON中?即JSON请求是{"Email": "y@y.com", "Phone": null}还是{"Email": "y@y.com"}?

我们如何仅通过查看解组为结构体a的JSON来判断?

是否有另一种方法可以通过REST进行更新(具有PATCH语义)?我正在寻找一种通用的方法来实现它(不限于特定的结构体)。

英文:
type A struct {
    Id int64
    Email sql.NullString
    Phone sql.NullString
}

Assume I have one record in the database

A{1, &quot;x@x.com&quot;, &quot;1112223333&quot;}

Send an update request via PUT

curl -X PUT -d &#39;{&quot;Email&quot;: &quot;y@y.com&quot;, &quot;Phone&quot;: null}&#39; http://localhost:3000/a/1 

Here is the psuedo algorithm that would work with a full PUT request (i.e. update all fields of the record A - but it will cause difficulties with the PATCH request semantics - delta update)

-- Unmarshal json into empty record

  a := A{}
  json.Unmarshal([]byte(request.body), &amp;a)

-- Load the record from the database

aFromDb = &lt;assume we got record from db&gt; //A{1, &quot;x@x.com&quot;, &quot;1112223333&quot;}

-- Compare a and aFromDB

-- Notice the email change and set it on aFromDb - ok

-- Notice the phone number change -- but wait! Was it set to NULL in the JSON explicitly or was it not even included in the JSON? i.e. was the json request - {"Email": "y@y.com", "Phone": null} or was it {"Email": "y@y.com"}?

How can we tell by just looking at the unmarshaled json into the struct a?

Is there another method to do the update via rest (with patch semantics)? I am looking for a generic way to do it (not tied to a particular struct).

答案1

得分: 1

我为此目的创建了一个单独的数据类型。这个例子是针对int64(实际上是字符串编码的int64),但你也可以很容易地将其更改为字符串。其背后的思想是,只有当值在JSON中存在时,才会调用UnmarshalJSON方法。该类型将实现MarshalerUnmarshaler接口。

// 用作JSON类型
//
// Set标志指示类型是否已进行了解组
type RequiredInt64 struct {
    Set   bool
    Int64 int64
}

func (r RequiredInt64) MarshalJSON() ([]byte, error) {
    lit := strconv.FormatInt(r.Int64, 10)
    return json.Marshal(lit)
}

func (r *RequiredInt64) UnmarshalJSON(raw []byte) error {
    var lit string
    var err error
    if err = json.Unmarshal(raw, &lit); err != nil {
        return err
    }
    r.Int64, err = strconv.ParseInt(lit, 10, 64)
    if err != nil {
        return err
    }
    r.Set = true
    return nil
}

因此,如果Set为false,你就知道该值在JSON中不存在。

英文:

I created a separate datatype for this purpose. This example is for an int64 (actually string-encoded int64), but you can easily change it to a string as well. The idea behind it is, that the UnmarshalJSON method will only be called if the value is present in the JSON. The type will implement the Marshaler and the Unmarshaler.

// Used as a JSON type
//
// The Set flag indicates whether an unmarshaling actually happened on the type
type RequiredInt64 struct {
	Set   bool
 	Int64 int64
}

func (r RequiredInt64) MarshalJSON() ([]byte, error) {
	lit := strconv.FormatInt(r.Int64, 10)
	return json.Marshal(lit)
}

func (r *RequiredInt64) UnmarshalJSON(raw []byte) error {
	var lit string
	var err error
	if err = json.Unmarshal(raw, &amp;lit); err != nil {
		return err
	}
 	r.Int64, err = strconv.ParseInt(lit, 10, 64)
    if err != nil {
    	return err
    }
	r.Set = true
	return nil
}

So, if Set is false, you know that the value was not present in the JSON.

答案2

得分: 0

尝试将这些标签添加到结构体中:

type A struct {
    Id int64 `json:"Id,omitempty"`
    Email sql.NullString `json:"Email,omitempty"`
    Phone sql.NullString `json:"Phone,omitempty"`
}

这样,如果你进行序列化操作并且字段为空,那么生成的 JSON 将不包含该字段。

但是,在反序列化时,该字段将具有一个值,或者它将具有该类型的默认值(指针为 Nil,字符串为空字符串)。

英文:

Try adding this tags to the struct:

type A struct {
    Id int64 `json:&quot;Id,omitempty&quot;`
    Email sql.NullString `json:&quot;Email,omitempty&quot;`
    Phone sql.NullString `json:&quot;Phone,omitempty&quot;`
}

In this way if you are serializing and the field is empty then the json will not contain the field.

When deserializing though the field will have a either a value or it will have the default value for the type (Nil for the pointer or empty string for strings).

答案3

得分: 0

你可以自己编写结构体的编组/解组代码,并对原始响应进行处理,尽管这可能不太明显,需要检查这些函数在处理什么。

另外,你可以在字段中不使用omitempty,并强制使用null填充的字段。

或者,也可以考虑使用不同类型的修补方法,比如http://jsonpatch.com/,这种方法更明确地描述了修改的性质。这将要求客户端对更改的状态有更深入的理解,而不仅仅是用于put请求。

英文:

You could potentially write your own marshalling/uinmarshalling of your struct and react to the raw response within, although it might be non-obvious witgh inspection what that those functions are manipulating.

Alternatively, you could not omitempty within your fields and force null populated fields.

Or, maybe leveraging a different flavor of patching, perhaps http://jsonpatch.com/, which is more explicit in the nature of your modifications. This would require the client to be more understanding of the state of changes than say for a put.

huangapple
  • 本文由 发表于 2014年5月23日 07:34:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/23818768.html
匿名

发表评论

匿名网友

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

确定