2017-08-10 31 views
10

正常工作,從版本2.5.7 REST的數據不能正確執行PUT請求要更新的具有相關的資源資源。與按預期工作的PATCH請求不同!例如,PersonAddres有多對一的關聯。如果我們使用SDR v.2.5.6(Spring Boot v.1.4.3)執行PUT請求,那麼一切正常。但是,如果我們切換到2.5.7版本(即春季啓動v.1.4.4),那麼我們得到一個錯誤:春季數據REST - PUT請求沒有因爲v.2.5.7

Can not construct instance of Address: no String-argument constructor/factory method to deserialize from String value

同樣的情況,與其他類型的關聯,例如一個一對多(單向和雙向) - 請參閱我的代碼和測試example application

這個問題出現在全部自1.4.4以來的Spring Boot版本,包括最新的穩定1.5.6版本,以及最新的2.0.0-SNAPSHOT版本!

要解決這個問題,我們可以切換到SDR v.2.5.6(Spring Boot v.1.4.3)。

我已經準備請求的郵差收集來幫助你的問題上發揮:SDR PUT Issue

UPDATE 2017年8月14日

我發現如何避免錯誤Can not construct instance of Address: no String-argument constructor/factory method to deserialize from String value

由於我在這個項目中使用Lombok, 有必要只是告訴龍目島使用@ConstructorProperties註釋中 generated constructors壓制。 所以我在'lombok.config'文件中設置了lombok.anyConstructor.suppressConstructorProperties=true,錯誤消失了。

不幸的是新問題被發現 - PUT請求根本不會更新關聯對象

下面的例子演示了這一點。當我們試圖通過將他的地址從addresses/1(初始值)更改爲addresses/2來更新Person時 - 那麼它將保持不變:addresses/1!除了之前的問題,這個版本還存在於彈性引導自1.4.4(SDR - 從v.2.5.7)開始的全部版本中。

我調試我的項目,發現上述問題的原因是隱藏在方法DomainObjectReader#mergeForPut(見its source) - 它用新的從未替換相關的資源。

在我發佈Spring JIRA的這個問題之前,請在這裏舉報如果您的項目中存在此問題,您對此有何看法

你可以得到我的測試here並在你的項目中檢查 - 測試是'獨立的',並不依賴於其他類/模塊(我只希望排除H2)。

@Entity 
public class Person { 

    private String name; 

    @ManyToOne 
    private Address address; 

    // other stuff 
} 

@Entity  
public class Address { 

    private String street; 

    // other stuff 
} 

嘗試更新的人:

PUT http://localhost:8080/api/persons/1 
{ 
    "name": "person1u", 
    "address": "http://localhost:8080/api/addresses/2" 
} 

獲取正確的響應:

{ 
    "name": "person1u", 
    "_links": { 
     "self": { 
      "href": "http://localhost:8080/api/persons/1" 
     }, 
     "person": { 
      "href": "http://localhost:8080/api/persons/1" 
     }, 
     "address": { 
      "href": "http://localhost:8080/api/persons/1/address" 
     } 
    } 
} 

然後檢查人的 '新' 的地址 - 沒有更新地址:

GET http://localhost:8080/api/persons/1/address 
{ 
    "street": "address1", 
    "_links": { 
     "self": { 
      "href": "http://localhost:8080/api/addresses/1" 
     }, 
     "address": { 
      "href": "http://localhost:8080/api/addresses/1" 
     } 
    } 
} 

UPDATE 2017年8月24日

感謝Scott C. answer,事實證明,SDR具有錯誤,其在兩張票描述:DATAREST-1001DATAREST-1012

+0

鏈接「http:// localhost:8080/api/persons/1/address」(v.2.5.6)的對象是什麼? – Andrew

+0

@AndrewTobilko地址1:'{ 「街道」: 「地址1」, 「_links」:{ 「自我」:{ 的 「href」: 「HTTP://本地主機:8080/API /地址/ 1」 } , 「地址」:{ 的 「href」: 「HTTP://本地主機:8080/API /地址/ 1」 }} } ' – Cepr0

+0

我不知道爲什麼它的第一個版本的作品。應該拋出同樣的異常,因爲'Address'實例不能從單個'String'構造。什麼是「BaseEntity」? – Andrew

回答

4

看起來像問題already been reported as a bug: - 請驗證。盡我所知,這是您上面報告的問題。

請注意,我正在修改我以前的答案是這個錯誤報告。

+0

謝謝你的迴應!我知道PUT和PATCH之間的區別。你完全正確 - PUT必須用新的實體替換實體,完全是簡單的字段和對其他對象的引用。正如[RFC2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6)所說:**封閉的實體**應該被認爲是一個**的修改版本駐留在原始服務器**上。正如你所看到的,'Person'對象的字段'name'被替換爲新的'person1u'值,但是'address'引用 - 不是。所以我認爲 - 這實際上是一個錯誤,它出現在1.4.4版的SB中 – Cepr0

+2

這並不能回答這個問題。要批評或要求作者澄清,請在其帖子下方留言。 - [來自評論](/ review/low-quality-posts/17106932) – Adonis

+0

我不確定它是否與此處相同。是的,它可能是相關的,但是你鏈接的錯誤只是關於集合,而這個錯誤甚至會影響非集合資源關聯(例如@ManyToOne關係) –

3

我同意你這是Spring Data REST中的一個錯誤,它應該被報告。

我的項目中存在同樣的問題,通過PATCH請求更新實體的工作正常,但PUT請求只更新給定實體的字段,但不更新其相關資源的字段。

爲什麼我認爲這是一個錯誤?

  • 由於人們正確地指出,PUT應該用於與修改後的版本,這表明它應該是工作,如果你提供資源的所有領域取代的資源作爲一個整體(如在POST請求中)。然而,在當前的Spring Data REST版本中,只更新了實體的簡單字段,並且關聯的資源保持不變,這使得PUT請求只能「部分工作」,並且這肯定不是預期的行爲(即使它滿足RFC) 。此外,Spring Data REST甚至允許您執行部分PUT請求,即通過PUT工作更新您的姓名字段。但它不適用於地址。
  • 這意味着,春季數據REST並不完全工作RFC如何指定它(這可能是另一個辯論),但它也沒有提供一致的使用 - 更新一個野外工作和更新等做時不是沒有任何錯誤的跡象。

爲了記錄,我正在使用Spring Data REST 2.6.3。

+0

謝謝您的意見!我已經開始懷疑這個問題的客觀性了......) – Cepr0

+0

在這個[演示](https://speakerdeck.com/olivergierke/hypermedia-apis-with-spring-5?slide=20)Olver Gierke don' t使用PUT來更新/替換資源。也許我們不知道什麼?)) – Cepr0

+0

那麼從[鏈接](https://stackoverflow.com/questions/28459418/rest-api-put-vs-patch-with-real-life-examples)斯科特C.張貼它實際上似乎是PUT是PATCH推出後不太有用。唯一合理的用例似乎是當你要求這個請求是冪等的。所以可能PATCH應該首選_common_更新。 但它並沒有改變它是一個錯誤的事實,因爲PUT不能按預期工作。 –