1.REST(Representational State Transfer)
用來規范應用如何在 HTTP 層與 API 提供方進行數據交互
REST約束
1.客戶端-服務器結構 2.無狀態 3.可緩存 4.分層的系統 5.按需代碼(可選) 6.統一接口。
該約束是 REST 服務的基礎,是客戶端和服務器之間的橋梁。該約束又包含下面4個子約束: 資源標識符。每個資源都有各自的標識符。客戶端在請求時需要指定該標識符。在 REST 服務中,該標識符通常是 URI。客戶端所獲取的是資源的表達(representation),通常使用 XML 或 JSON 格式。 通過資源的表達來操縱資源。客戶端根據所得到的資源的表達中包含的信息來了解如何操縱資源,比如對資源進行修改或刪除。 自描述的消息。每條消息都包含足夠的信息來描述如何處理該消息。 超媒體作為應用狀態的引擎(HATEOAS)。客戶端通過服務器提供的超媒體內容中動態提供的動作來進行狀態轉換。
2.HATEOAS(The Hypermedia As The Engine Of Application Statue)
是REST架構的主要約束
REST成熟的模型
第一個層次(Level 0)的 Web 服務只是使用 HTTP 作為傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬於此類。 第二個層次(Level 1)的 Web 服務引入了資源的概念。每個資源有對應的標識符和表達。 第三個層次(Level 2)的 Web 服務使用不同的 HTTP 方法來進行不同的操作,並且使用 HTTP 狀態碼來表示不同的結果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。 第四個層次(Level 3)的 Web 服務使用 HATEOAS。在資源的表達中包含了鏈接信息。客戶端可以根據鏈接來發現可以執行的動作。
根據REST 成熟度模型中可以看到,使用 HATEOAS 的 REST 服務是成熟度最高的,也是推薦的做法
RESTful API最好做到Hypermedia,或HATEOAS,即返回結果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應該做什么
eg:
{ "links": { "self": { "href": "http://api.com/items" }, "item": [ { "href": "http://api.com/items/1" }, { "href": "http://api.com/items/2" } ] "data": [ {"itemName":"a"}, {"itemName":"b"} ] }
3.HAL(Hypertext Application Language)
HAL是一種簡單的格式,為 API 中的資源提供簡單一致的鏈接
HAL可以用來實現HATEOAS
HAL 模型包括:
鏈接
內嵌資源
狀態

HAL專為構建API而設計,在這些API中,客戶端通過以下鏈接在客戶端中瀏覽資源
4.spring-boot-starter-data-rest使用Spring Boot構建RESTful API
Spring Data REST是基於Spring Data的repository之上,可以把 repository 自動輸出為REST資源
Spring Data REST把我們需要編寫的大量REST模版接口做了自動化實現,並符合HAL的規范
(1)添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
(2)示例
實體類User
package com.example.demo.model; import lombok.*; import org.hibernate.annotations.CreationTimestamp; import javax.persistence.*; import java.util.Date; @Entity @Table(name = "users") @Data @Builder @ToString(callSuper = true) @NoArgsConstructor @AllArgsConstructor public class Users { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; @Column(updatable = false) @CreationTimestamp private Date createTime; }
UsersRepository
package com.example.demo.repository; import com.example.demo.model.Users; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface UsersRepository extends JpaRepository<Users,Integer> { }
這樣就已經提供了一個關於user的rest api,簡單的增、刪、改、查都有了。不用寫Controller,spring已經實現了
啟動項目
GET http://127.0.0.1:8080
{ "_links": { "userses": { "href": "http://127.0.0.1:8080/userses{?page,size,sort}", "templated": true }, "profile": { "href": "http://127.0.0.1:8080/profile" } } }
分頁+排序查詢
GET http://127.0.0.1:8080/userses?page=1&size=2&sort=createTime
{ "_embedded": { "userses": [ { "name": "Nana", "createTime": "2020-04-07T02:10:12.469+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/userses/5" }, "users": { "href": "http://127.0.0.1:8080/userses/5" } } }, { "name": "xyz", "createTime": "2020-04-07T02:10:12.469+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/userses/3" }, "users": { "href": "http://127.0.0.1:8080/userses/3" } } } ] }, "_links": { "first": { "href": "http://127.0.0.1:8080/userses?page=0&size=2&sort=createTime,asc" }, "prev": { "href": "http://127.0.0.1:8080/userses?page=0&size=2&sort=createTime,asc" }, "self": { "href": "http://127.0.0.1:8080/userses" }, "next": { "href": "http://127.0.0.1:8080/userses?page=2&size=2&sort=createTime,asc" }, "last": { "href": "http://127.0.0.1:8080/userses?page=2&size=2&sort=createTime,asc" }, "profile": { "href": "http://127.0.0.1:8080/profile/userses" } }, "page": { "size": 2, "totalElements": 5, "totalPages": 3, "number": 1 } }
查詢某一個
GET http://127.0.0.1:8080/userses/5
{ "name": "Nana", "createTime": "2020-04-07T02:10:12.469+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/userses/5" }, "users": { "href": "http://127.0.0.1:8080/userses/5" } } }
新增
POST http://127.0.0.1:8080/userses

修改
PUT http://127.0.0.1:8080/userses/481

刪除
DELETE http://127.0.0.1:8080/userses/481
(3)其他
注解 @RepositoryRestResource指定切入點
eg:
映射到 /user上
添加自定義查詢
findByName
findByNameContaining
package com.example.demo.repository; import com.example.demo.model.Users; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.stereotype.Repository; import java.util.List; @RepositoryRestResource(collectionResourceRel = "user", path = "user") public interface UsersRepository extends JpaRepository<Users,Integer> { List<Users> findByName(@Param("name") String name); List<Users> findByNameContaining(@Param("name") String name); }
注:
方法的定義,參數要有 @Param 注解
重新啟動項目
GET http://127.0.0.1:8080/user
{ "_embedded": { "user": [ { "name": "Lili", "createTime": "2020-04-07T02:50:27.501+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/user/1" }, "users": { "href": "http://127.0.0.1:8080/user/1" } } }, { "name": "Fiona", "createTime": "2020-04-07T02:50:27.508+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/user/2" }, "users": { "href": "http://127.0.0.1:8080/user/2" } } }, { "name": "xyz", "createTime": "2020-04-07T02:50:27.508+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/user/3" }, "users": { "href": "http://127.0.0.1:8080/user/3" } } } } ] }, "_links": { "self": { "href": "http://127.0.0.1:8080/user{?page,size,sort}", "templated": true }, "profile": { "href": "http://127.0.0.1:8080/profile/user" }, "search": { "href": "http://127.0.0.1:8080/user/search" } }, "page": { "size": 20, "totalElements": 5, "totalPages": 1, "number": 0 } }
GET http://127.0.0.1:8080/user/search
{ "_links": { "findByNameContaining": { "href": "http://127.0.0.1:8080/user/search/findByNameContaining{?name}", "templated": true }, "findByName": { "href": "http://127.0.0.1:8080/user/search/findByName{?name}", "templated": true }, "self": { "href": "http://127.0.0.1:8080/user/search" } } }
查詢 name=xyz
GET http://127.0.0.1:8080/user/search/findByName?name=xyz
{ "_embedded": { "user": [ { "name": "xyz", "createTime": "2020-04-07T03:14:11.921+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/user/3" }, "users": { "href": "http://127.0.0.1:8080/user/3" } } } ] }, "_links": { "self": { "href": "http://127.0.0.1:8080/user/search/findByName?name=xyz" } } }
查詢 name 包含i的
GET http://127.0.0.1:8080/user/search/findByNameContaining?name=i
{ "_embedded": { "user": [ { "name": "Lili", "createTime": "2020-04-07T03:14:11.913+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/user/1" }, "users": { "href": "http://127.0.0.1:8080/user/1" } } }, { "name": "Fiona", "createTime": "2020-04-07T03:14:11.920+0000", "_links": { "self": { "href": "http://127.0.0.1:8080/user/2" }, "users": { "href": "http://127.0.0.1:8080/user/2" } } } ] }, "_links": { "self": { "href": "http://127.0.0.1:8080/user/search/findByNameContaining?name=i" } } }
配置
在application.properties 中配置
給所有的接口添加統一的前綴
spring.data.rest.base-path=/rest
添加/更新成功時是否返回添加/更新記錄
spring.data.rest.return-body-on-create=true spring.data.rest.return-body-on-update=true
注:
如果為false,則返回空白
