Spring Data REST不完全指南(二)
上一篇文章介紹了Spring Data REST的功能及特徵,以及演示了如何在項目中引入Spring Data REST並簡單地啟動演示了Spring Data REST項目。在本文中,我們將深入了解Spring Data REST的特性,以此來滿足我們日常api開發工作的要求。
如果僅僅是上一篇文章中對Spring Data REST的使用,那無法做到在日常開發中使用Spring Data REST,所以在上一篇文章中,我們列出了日常api開發中的一些必要功能:
需要滿足的一些要求:
1.針對欄位級別,方法級別,類級別進行限制(禁止某些欄位,方法,介面的對外映射)。
2.對數據增刪改查的限制(禁止某些請求方法的訪問)。
3.能個性化定義請求的路徑。
4.對所傳參數進行值校驗。
5.響應統一處理。
6.異常處理。
7.數據處理的切面。
➡️本文,將演示這些要求中的前三個要求。
針對介面級別,方法級別,欄位級別進行訪問限制
所謂的訪問限制,這裡我們的目的是指定某些資源不對外暴露,Spring Data REST使用註解來實現各級別的訪問限制。
介面級別的訪問限制:
@RepositoryRestResource(exported = false)
public interface TenantRepository extends CrudRepository<Tenant, Long> {
Page<Tenant> findAllByNameContaining(String name, Pageable page);
Page<Tenant> findAllByIdCardContaining(String idCard, Pageable page);
Tenant findFirstByMobile(String mobile);
Tenant findFirstByIdCard(String idCard);
}
Spring Data REST中我們在介面級別增加@RepositoryRestResource(exported = false)
來實現介面及介面中的所有方法不對外暴露,從而限制訪問。
方法級別的訪問限制:
public interface TenantRepository extends CrudRepository<Tenant, Long> {
Page<Tenant> findAllByNameContaining(String name, Pageable page);
Page<Tenant> findAllByIdCardContaining(String idCard, Pageable page);
Tenant findFirstByMobile(String mobile);
@RestResource(exported = false)
Tenant findFirstByIdCard(String idCard);
}
上述程式碼中,我們使用@RestResource(exported = false)
註解在指定的方法上,從而實現該方法不對外暴露。
欄位級別的訪問限制
@Entity
@Data
@Accessors(chain = true)
public class Tenant {
@Id
@GeneratedValue
private Long id;
private String name;
//隱私資訊不需要暴露
@JsonIgnore
private String idCard;
private String mobile;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime rentDateTime;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
private House house;
public Tenant() {
}
public Tenant(String name, String idCard, String mobile, LocalDateTime rentDateTime, House hous) {
this.name = name;
this.idCard = idCard;
this.mobile = mobile;
this.rentDateTime = rentDateTime;
this.house = hous;
}
}
在上述程式碼中,我們在idCard欄位上增加了@JsonIgnore
註解,從而實現該欄位不對外暴露。
如上圖,我們在HAL Browser中看到,輸出的租客數據中不再包含idCard欄位。
使用Projections和Excerpts來實現訪問限制
@Projection(name = "mobileAndName", types = {Tenant.class})
public interface TenantProjection {
String getName();
String getMobile();
}
如上,首先我們聲明一個投影(Projection),在上面的投影中,我們使用指定欄位的get方法來對外暴露需要暴露的欄位name,mobile。
此時,我們訪問HAL Browser
可以看到Spring Data REST此時提供了一個投影的鏈接。
此時我們查詢指定租客類的投影//localhost:8080/tenants/1?projection=mobileAndName
{
"name": "王一",
"mobile": "186****3331",
"_links": {
"self": {
"href": "//localhost:8080/tenants/1"
},
"tenant": {
"href": "//localhost:8080/tenants/1{?projection}",
"templated": true
}
}
}
可以看到只顯示了我們投影的欄位。
⚠️:我們聲明的投影介面需要和數據類在同一個包中。
⚠️:否則,我們需要增加配置類,來告訴Spring Data REST投影介面的位置,如下圖
@Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.getProjectionConfiguration().addProjection(TenantProjection.class);
}
數據內聯
有時候,我們可能需要單獨對House類進行數據操作,所以我們會聲明一個HouseRepository
public interface HouseRepository extends CrudRepository<House, Long> {
}
此時,我們訪問某一租客數據(//localhost:8080/tenants/1)時,
此時,house的數據就不會內聯在Tenant裡面。但是我們並不想要這種效果,我們希望house還是內聯在Tenant中的。我們可以使用Projection來解決此問題。
@Projection(name = "NameAndHouse", types = {Tenant.class})
public interface OnlyNameProjection {
String getName();
House getHouse();
}
如上,我們聲明了一個NameAndHouse投影。然後我們使用@RepositoryRestResource(excerptProjection = OnlyNameProjection.class)
註解放在TenantRepository介面上。
@RepositoryRestResource(excerptProjection = OnlyNameProjection.class)
public interface TenantRepository extends CrudRepository<Tenant, Long> {
...
}
此時,我們訪問NameAndHouse投影(//localhost:8080/tenants/1?projection=NameAndHouse)
{
"name": "王一",
"house": {
"houseNumber": "1101",
"owner": "張三",
"idCard": "330521******1"
},
"_links": {
"self": {
"href": "//localhost:8080/tenants/1"
},
"tenant": {
"href": "//localhost:8080/tenants/1{?projection}",
"templated": true
},
"house": {
"href": "//localhost:8080/tenants/1/house"
}
}
}
可以看到,House已經被內聯進Tenant中。
對數據增刪改查的限制
Spring Data REST提供了對資源請求的限制,比如對特定請求方法的限制,對特定資源訪問的限制。
Repository對外暴露限制
有時候我們希望,我們在Repository中定義的某些數據操作方法不對外暴露。Spring Data REST提供了了四個級別的資源限制級別:
- ALL:公開所有Spring Data存儲庫,無論其Java可見性或注釋配置如何。
- DEFAULT:公開公共Spring數據存儲庫或使用
@RepositoryRestResource
顯式注釋的存儲庫,並且其導出屬性未設置為false。- VISIBILITY:無論注釋配置如何,僅公開公共Spring Data存儲庫。
- ANNOTATED:僅公開使用
@RepositoryRestResource
顯式注釋的Spring Data存儲庫,並且其導出屬性未設置為false。
如下程式碼實現了ANNOTATED級別的限制:
@Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.setRepositoryDetectionStrategy(RepositoryDetectionStrategy.RepositoryDetectionStrategies.ANNOTATED);
}
}
增刪改查限制
在RESTful中定義:
GET(GET方法返回單個實體)
PUT(PUT方法用提供的請求主體替換目標資源的狀態(存在則修改,不存在則新建)。)
PATCH(PATCH方法類似於PUT方法,但是部分更新資源狀態。)
DELETE(刪除資訊)
所以所謂的對增刪改查的限制實際上就是對請求方法的限制。
如下,我們對Tenant類進行了兩個操作
- PUT操作禁止新增,但可以修改。
- DELETE限制,也就是限制了刪除操作。
@Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
ExposureConfiguration exposureConfiguration = config.getExposureConfiguration();
exposureConfiguration.forDomainType(Tenant.class)
.disablePutForCreation()
.withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.DELETE));
}
}
如下為請求測試:
1.禁止DELETE請求
2.禁止PUT的新增操作
3.允許PUT的修改操作
個性化定義請求的路徑
Spring Data REST提供了個性化請求路徑的功能
自定義項目資源URI
默認情況下,項目資源的URI包含用於集合資源的路徑段,並附加了資料庫標識符。這樣一來,您就可以使用存儲庫的findOne(…)方法來查找實體實例。從Spring Data REST 2.5開始,可以通過使用RepositoryRestConfiguration上的配置API(在Java 8上首選)或通過將EntityLookup的實現註冊為應用程式中的Spring bean來自定義此屬性。 Spring Data REST會選擇它們並根據其實現來調整URI生成。
@Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup()
.forRepository(TenantRepository.class)
.withIdMapping(Tenant::getMobile)
.withLookup(TenantRepository::findFirstByMobile);
}
}
如上,我們個性化地指定了Tenant的標識符為mobile欄位,此時我們可以通過HAL Browser來看到路徑中的標識符變成了mobile欄位。
配置REST URL路徑
我們使用@RepositoryRestResource
和@RestResource
註解直接指定資源在路徑中的名字。
如下:
@RepositoryRestResource(path = "tenantPath")
public interface TenantRepository extends CrudRepository<Tenant, Long> {
@RestResource(path = "mobile")
Tenant findFirstByMobile(String mobile);
}
此時我們通過HAL Browser訪問此資源:
可以看到,被我們註解的findFirstByMobile對應的資源路徑已經被訂製化。
另外我們看到”links”中的屬性名還是findFirstByMobile,我們可以通過@RestResource(path = "mobile",rel = "mobile")
來個性化指定其名字,例如此註解中,我們指定了”links”中的findFirstByMobile屬性名為mobile。
總結
本文列出了介面開發常用的7個功能,並且演示如何實現針對介面級別,方法級別,欄位級別進行訪問限制,我們使用@RepositoryRestResource
,@RestResource
,@JsonIgnore
分別實現介面,方法,欄位級別的訪問限制,並且我們利用了Projections和Excerpts來實現自定義數據格式。我們還是實現了對數據增刪改查的限制,我們通過RepositoryDetectionStrategy的四個級別來控制數據介面的對外暴露,使用ExposureConfiguration來限制某些資源對特定請求方式的限制。最後我們使用RepositoryRestConfiguration以及@RepositoryRestResource
和@RestResource
註解實現了個性化定義請求的路徑。
本文程式碼示例://gitee.com/jeker8chen/spring-data-rest-in-practice.git
關注筆者公眾號,推送各類原創/優質技術文章 ⬇️