測試環境:
- OpenJDK 14
- Spring Boot 2.2.4
注意
這是在「REST 只有基本概念、Spring 碰不到一個月而且沒認真讀過文件」的情況下,在混亂的實做中得到的混亂廢話
因為 Spring 幾乎沒碰過,所以從頭 (明明就是鞋子) 開始講起。
Spring Boot 真的讚,pom.xml 加幾個 dependency,弄個 @SpringBootApplication
的 main class,就連 Tomcat(包含自動 reload)都幫你啟動完畢。瞬間把 Eclipse 那個越來越難用的 Servers view 給關了省的佔畫面(不過萬一要同時多個 webapp 運作可能還是… 算了到時候再說 XD)
@RestController
大概也是簡化的極限了,預設情況下連 return 值轉 JSON 變成 response 內容都自動幫你處理好,找不到可以抱怨的點(因為也沒寫過幾個… [逃])
Spring Data JPA 稍微棘手一點,自動掃 @Entity
class 一直沒成功,然後又想搞可以自訂檔案位置(而不是設定檔寫死)的 H2,所以稍微花了一點時間。不過已經比想像中的還要簡單很多了。
撇開一些個人龜毛的部份,從 Spring 程度幾乎等於 0 的初學者(但是會基本 Maven、有用過 Hibernate)、到弄出一個 REST controller 來對 DB 做 CRUD,大概就是 10hr 以內的光景。當然,這裡說的是 POC 等級的程度。 是說我寫出來的東西好像也都只有 POC 等級
至於 Spring Data REST 嘛… 一句話就可以結案:「@Repository
class 再加一個 @RepositoryRestResource(path = "foo")
」
立馬 /foo
就可以處理 GET / PUT… 了,就是這麼簡單~ 就是這麼神奇~
好,server side 收工!
才沒有這麼沒好… [眼神死]
第一個會炸到的問題是:「GET 的 response 要怎麼 unmarshal?」
不是直接把 List<Foo>
轉成 JSON 嗎?我也這麼希望,但就不是,而是長這樣(HATEOAS / HAL):
{
"_embedded" : {
"foos" : [ {
"name" : "FooName",
"_links" : {
"self" : {
"href" : "http://localhost:8080/foo/2"
},
"location" : {
"href" : "http://localhost:8080/foo/2"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/foo"
},
"profile" : {
"href" : "http://localhost:8080/profile/foo"
}
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
真正的資料是在 ._embedded.foos
,這還好,不算啥大事,而且有分頁功能好棒棒。問題是裡頭的 Foo
結構沒有掛 @Id
的 field。雖然這個去狗一下可以找到解決辦法,但終究得花上一些功夫才有可能轉回 Foo
instance… (沒有實際測試,好像還得設法讓 gwt-jackson 忽略 "_links"
才能正常運作… )
這也許是用 GWT 的人才會有的困擾,畢竟 GWT 的賣點之一就是 server / client 可以使用同一份 code。如果 client 用純 JS 或許就沒問題,反正本來 就是假的 OOP 就沒有 code 可以用,要抓 JSON 中的值也可以用字串隨便指到想要的位置…
目前的作法是直接跳過 @RepositoryRestResource
提供的 URI,自己另外寫一個吐單純 JSON 的 REST controller… 💃
第二個炸點就更麻煩了:「不會處理 @ManyToOne
(其他沒測)的 field」。
假設前頭的 Foo
是這樣:
@Entity
public class Foo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne
private Wtf wtf;
}
GET 得到的 JSON 不會出現 wtf
、POST 也會跳過這個 field 不處理,直接塞 null。
這樣設計有沒有道理?應該有,至少 GET 的傳輸量就可以少很多(都說 many to one 了嘛)。但是完全連轉都沒轉是不是合理?為什麼不像 ORM 時 wtf
會轉換成 WTF_ID
column 那樣做 JSON marshal?(還是說有神秘的設定檔可以改這行為?)(沒有試著找過,因為連關鍵字都不知道怎麼下 XD)
總之,沒用到就沒事,只要有用到 ManyToOne
的話,Spring Data REST 大概就是爛給你看。如果是剛出生沒多久的 project 或許還改得動,不然自己手寫 REST controller 可能還比較實在… 😱
沒有意義的結論
- Spring 真的強,強到「完全不敢想像背後程式是怎麼寫」的那種強
- 為什麼 HATEOAS / HAL 會設計成這樣子
(之前接觸 FHIR 的時候好像也是這付德性,原本還以為是 FHIR 亂搞…)
- 目前看起來 GWT 這邊沒有 library 可以對付 HATEOAS / HAL,不知道 JS 那邊…
算了,我不想知道
- Spring Data REST (至少)還有 validate 等功能要 try… 😭
最後就是… 嗯… 我真的很懷念 GWT RPC。要是有時程壓力,我一定馬上回頭用 GWT RPC… [遠目]