上一篇文章介紹了Spring Data REST的功能及特徵,以及演示瞭如何在項目中引入Spring Data REST並簡單地啓動演示了Spring Data REST項目。在本文中,咱們將深刻了解Spring Data REST的特性,以此來知足咱們平常api開發工做的要求。java
若是僅僅是上一篇文章中對Spring Data REST的使用,那沒法作到在平常開發中使用Spring Data REST,因此在上一篇文章中,咱們列出了平常api開發中的一些必要功能:git
須要知足的一些要求:
1.針對字段級別,方法級別,類級別進行限制(禁止某些字段,方法,接口的對外映射)。
2.對數據增刪改查的限制(禁止某些請求方法的訪問)。
3.能個性化定義請求的路徑。
4.對所傳參數進行值校驗。
5.響應統一處理。
6.異常處理。
7.數據處理的切面。spring
➡️本文,將演示這些要求中的前三個要求。數據庫
所謂的訪問限制,這裏咱們的目的是指定某些資源不對外暴露,Spring Data REST使用註解來實現各級別的訪問限制。json
@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)
來實現接口及接口中的全部方法不對外暴露,從而限制訪問。api
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)
註解在指定的方法上,從而實現該方法不對外暴露。app
@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
註解,從而實現該字段不對外暴露。ide
如上圖,咱們在HAL Browser中看到,輸出的租客數據中再也不包含idCard字段。測試
@Projection(name = "mobileAndName", types = {Tenant.class}) public interface TenantProjection { String getName(); String getMobile(); }
如上,首先咱們聲明一個投影(Projection),在上面的投影中,咱們使用指定字段的get方法來對外暴露須要暴露的字段name,mobile。this
此時,咱們訪問HAL Browser
能夠看到Spring Data REST此時提供了一個投影的連接。
此時咱們查詢指定租客類的投影http://localhost:8080/tenants/1?projection=mobileAndName
{ "name": "王一", "mobile": "186****3331", "_links": { "self": { "href": "http://localhost:8080/tenants/1" }, "tenant": { "href": "http://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> { }
此時,咱們訪問某一租客數據(http://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投影(http://localhost:8080/tenants/1?projection=NameAndHouse)
{ "name": "王一", "house": { "houseNumber": "1101", "owner": "張三", "idCard": "330521******1" }, "_links": { "self": { "href": "http://localhost:8080/tenants/1" }, "tenant": { "href": "http://localhost:8080/tenants/1{?projection}", "templated": true }, "house": { "href": "http://localhost:8080/tenants/1/house" } } }
能夠看到,House已經被內聯進Tenant中。
Spring Data REST提供了對資源請求的限制,好比對特定請求方法的限制,對特定資源訪問的限制。
有時候咱們但願,咱們在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類進行了兩個操做
@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包含用於集合資源的路徑段,並附加了數據庫標識符。這樣一來,您就可使用存儲庫的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字段。
咱們使用@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
註解實現了個性化定義請求的路徑。
本文代碼示例:https://gitee.com/jeker8chen/spring-data-rest-in-practice.git
關注筆者公衆號,推送各種原創/優質技術文章 ⬇️