注意这个是DbContext的方法而不是DbSet的方法,它会追踪city,然后把它的ModelState设置为Modified。
测试:
OK.
下面做另一个测试,如果body里面的对象缺少某些属性呢?(由于对象本身只有一个属性,我就传递一个无属性对象吧- -!):
操作结果依然是没问题的,使用GET反查一下:
name属性就变成了null,这不难理解,PUT是整体性更新,如果传递的参数对象缺少某些属性,那么这些属性的值就相当于是null,也会整体更新给Model。
由于这种原因,PUT用的就比较少,不可能为了更新对象中的一个属性而把对象所有的属性值都传递回去。
所以PATCH(局部更新)就应用的比较广泛了。
PUT不具有安全性,因为每次执行PUT都会改变资源。
但是PUT具有等幂性,这个很好理解,多次执行同一个PUT请求后,结果是一样的。
更新集合资源
跟删除集合资源一样,针对某个路由进行集合请求是合法的,但是这也意味着传进来的集合要整体代替原有的集合,也就是说原有集合里面的对象都应该删除,然后传进来集合的对象挨个再添加进去。但是这样的话是有副作用的,每次执行的结果其实是不一样的。此外这种集合更新也是具有较大的破坏性,所以一般不这么做。
更新或创建资源
我记得好像在使用老版本Entity Framework做种子数据的时候,经常使用一个扩展方法叫做AddOrUpdate(),也就是如果数据存在那就更新它,否则就创建它。
在REST API里,我们有时也会遇到这样的需求。我们暂时把这个方法叫做Upsert (Update + Insert) 。那么问题来了应该使用POST还是PUT呢?
PUT请求会发送到现有资源的URI上,如果资源不存在就返回404。
而POST用于创建资源,所以肯定不知道该资源的URI(是指GET的URI)。
但是如果API的消费者可以创建资源,那么,PUT请求可以被发送到一个暂时不存在的资源的URI上;如果资源不存在,那就创建它,否则就修改它。
所以感觉使用PUT作为Upsert的HTTP方法比较合适一些。
但是如果使用自增类主键Id的话,这种情况就不适合了。
下面我们假设City的Id不是自增的,那么我们可以这样修改一下Update方法:
由于我的例子主键是自增的,所以不适合Upsert。我就不测试了。
但是总体的思路就是这样,注意里面新增和修改返回的结果略有不同。
PATCH 局部更新资源
使用PUT最整体更新,缺点还是很明显的,所以我更多使用的是PATCH局部更新。
HTTP PATCH请求的body部分需要使用RFC 6902 (JSOn Patch)这个标准来进行描述。
而PATCH请求的media type应该设定为 "application/json-patch+json"。
PATCH请求的body是一个操作的数组:
这个例子里面有两个操作:
第一个是“replace”操作(op的值就是操作的类型),path代表着资源的属性名,value表示的是更新后的值。
第二个操作类型是“remove”,表示要删除资源的某个属性的值,例子里是name属性。
JSON PATCH的操作类型主要有六种:
添加:{“op”: "add", "path": "/xxx", "value": "xxx"},如果该属性不存,那么就添加该属性,如果属性存在,就改变属性的值。这个对静态类型不适用。
删除:{“op”: "remove", "path": "/xxx"},删除某个属性,或把它设为默认值(例如空值)。
替换:{“op”: "replace", "path": "/xxx", "value": "xxx"},改变属性的值,也可以理解为先执行了删除,然后进行添加。
复制:{“op”: "copy", "from": "/xxx", "path": "/yyy"},把某个属性的值赋给目标属性。