Spring Data Rest設(shè)計的目的是消除curd的模板代碼淆储,減少程序員的刻板的重復勞動冠场,盡管擁有強大的功能和精妙的設(shè)計,但它作為Spring Data系列產(chǎn)品本砰,終究不能完全代替?zhèn)鹘y(tǒng)的SpringMVC碴裙,其特點也如Spring Data JPA之與Spring Data JDBC等低封裝度的產(chǎn)品,高度封裝了許多細節(jié)点额,但在用法上有它自己的一套規(guī)則青团。
〇、使用前提條件
此框架需配合其他ORM框架使用如SDJ咖楣,使用時給人的直觀感受就是暴露了資源倉庫督笆。
一、普通的crud
這部分作為SDR的最大亮點诱贿,它的實現(xiàn)形式就是完全消除模板代碼娃肿,我們需要做的就是訪問對應(yīng)的資源倉庫地址實現(xiàn)相關(guān)操作
增刪改查動作分別對應(yīng)四種請求類型:post、delete珠十、update/patch料扰、get
增:直接攜帶請求體請求倉庫地址;如有對應(yīng)關(guān)系焙蹭,一對多的多端的關(guān)聯(lián)對象屬性應(yīng)為對應(yīng)的關(guān)聯(lián)對象的倉庫地址(SDR和SDJ會將該鏈接轉(zhuǎn)為相關(guān)對象儲存)晒杈,多對一的一端的關(guān)聯(lián)對象集合屬性應(yīng)為要儲存的關(guān)聯(lián)對象集合。
刪:直接請求資源地址孔厉;若被刪對象的關(guān)聯(lián)對象類(一對多的“一”端)的關(guān)系注解處(如@OneToMany)寫明了級聯(lián)操作(如:CascadeType.REMOVE)則關(guān)聯(lián)的對象將被一并刪除拯钻。
改:直接請求資源地址帖努,put將完全替換原對象,patch將修改指定了值的對象屬性(忽略空值)粪般;然而只有patch請求能修改關(guān)聯(lián)關(guān)系拼余,put請求將忽略關(guān)聯(lián)關(guān)系對象。
查:
Spring Data REST 在查詢具有對象屬性(已使用@ManyToOne等注解建立關(guān)聯(lián)關(guān)系)的對象時亩歹,默認將對象屬性用鏈接代替匙监,這體現(xiàn)了REST服務(wù)概念中的“資源”的特點,但是當遇到需要同時查詢多個具有關(guān)聯(lián)關(guān)系的對象及其關(guān)聯(lián)的對象的場景的時候小作,不免要發(fā)送多個請求來查詢關(guān)聯(lián)對象亭姥,Spring官方對這種場景的解決方案是使用@Projection注解自定義返回字段,此查詢法適用于查詢資源集合和需要顯示指定屬性的資源對象顾稀。
使用步驟:
0致份、創(chuàng)建實體類,使用@OneToMany等注解建立好對象的關(guān)聯(lián)關(guān)系础拨,并創(chuàng)建對應(yīng)的Repository資源倉庫
1氮块、創(chuàng)建一個具有查詢對象屬性方法的接口,該接口內(nèi)的所有查詢方法返回的字段即為將來使用對應(yīng)api查詢時返回的Json屬性诡宗;接口包含的查詢方法名必須嚴格匹配getXXX格式(XXX即為對象屬性名)滔蝉,否則查詢結(jié)果無法映射,系統(tǒng)將報錯塔沃;該接口使用
@Projection(name="X",types={“XX.class”})
注解以表明調(diào)用該api的projection參數(shù)(X)和它關(guān)聯(lián)的類(XX)蝠引。例子如下
2、如需將該projection定義為默認的返回形式蛀柴,則在對應(yīng)的Repository倉庫上使用
@RepositoryRestResource(excerptProjection = XXXX.class)
注解螃概,但是這種方法將導致查詢單個對象時默認返回projection定義的形式。例子如下
3鸽疾、當需要使用自定義api查詢關(guān)聯(lián)對象時吊洼,需要在倉庫地址后加入projection=X參數(shù)
從源碼結(jié)構(gòu)中可以看出,其封裝了部分mvc的相關(guān)功能制肮,這體現(xiàn)了它最大的特點——“便捷”冒窍,然而這就不可避免地引入了額外的復雜度,我在使用時就必須根據(jù)它要求的用法豺鼻、它自身特點并考慮項目需求综液,劃分合適的項目結(jié)構(gòu),編寫邏輯代碼儒飒。
我這兩天遇到一個業(yè)務(wù)場景:管理員可以新增谬莹、修改用戶(密碼)信息,本以為結(jié)合spring security一起使用是很方便的,然而在編寫代碼時發(fā)現(xiàn)以下需要注意的地方:
1附帽、sdr默認的倉庫權(quán)限配置不靈活:常用的倉庫方法如GET users埠戳、GET user/{id}等,還有自定義的倉庫方法士葫,均無法直接在rest入口處配置鑒權(quán)檢查(因為sdr 自帶的mvc沒有提供相關(guān)接口)
解決:根據(jù)官方文檔信息得出折衷的解決辦法就是在倉庫方法上直接配置乞而,然而這樣做與我理解的傳統(tǒng)mvc配置鑒權(quán)的方式有出入送悔,優(yōu)先級最高的慢显,需要在“入口處”進行的權(quán)限檢查工作轉(zhuǎn)移到了持久層上。實際上欠啤,sdr在rest入口和持久層間有其他增強方法可配置(后面會講到)荚藻,這些地方常用來進行參數(shù)校驗,并且我將部分權(quán)限檢查移到了那里洁段,但是不論權(quán)限配置在這些增強方法上進行還是在上述持久層進行应狱,都不是如“傳統(tǒng)的”mvc結(jié)構(gòu)那樣在rest入口處進行校驗、檢查工作
2祠丝、需要針對sdr封裝的一系列功能進行額外配置:如對mvc配置的cors放行條件對sdr不起作用疾呻,(其他配置后續(xù)更新)
解決:
3、增強方法的使用上需要注意sdr對于對象的處理流程:我需要對修改密碼的用戶提交的新密碼進行加密写半,在使用由 @HandleBeforeSave 注解標注的方法時岸蜗,其傳入的對象經(jīng)過了以下處理:
1、從數(shù)據(jù)庫用id查出對象user0叠蝇;
2璃岳、將傳入的對象與user0進行比對;
3悔捶、更新user0中與接收的對象不相同的字段
這樣我不能簡單地對用戶的密碼進行加密或不加密铃慷,這個字段有可能是用戶設(shè)置的新密碼,有可能是數(shù)據(jù)庫中已加密的密碼(用戶未更新密碼)蜕该,我需要調(diào)用spring security提供的正則表達式判斷這個密碼到底是用戶提供的還是數(shù)據(jù)庫中原本 已加密的:
解決:
public void encodePassword(User user) {
Optional.of(user.getPassword())
? ? ? ? ? ? .filter(pw -> !BCRYPT_PATTERN.matcher(pw).matches())
.ifPresent(
pw ->user.setPassword(passwordEncoder.encode(pw))
);
}