使用SpringDataREST快速構建HAL風格的Restful應用

使用SpringDataREST快速構建HAL風格的Restful應用

前言

RESTFUL API簡介

REST(英文:Representational State Transfer,簡稱REST)描述了一個架構樣式的網絡系統,好比 web 應用程序。它首次出如今 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規範的主要編寫者之一。在目前主流的三種Web服務交互方案中,REST相比於SOAP(Simple Object Access protocol,簡單對象訪問協議)以及XML-RPC更加簡單明瞭,不管是對URL的處理仍是對Payload的編碼,REST都傾向於用更加簡單輕量的方法設計和實現。值得注意的是REST並無一個明確的標準,而更像是一種設計的風格。

經常使用的HTTP動詞有下面五個(括號裏是對應的SQL命令)。html

GET(SELECT) :從服務器取出資源(一項或多項)。
POST(CREATE):在服務器新建一個資源。
PUT(UPDATE) :在服務器更新資源(客戶端提供改變後的完整資源)。
PATCH(UPDATE) :在服務器更新資源(客戶端提供改變的屬性)。
DELETE(DELETE):從服務器刪除資源。
HEAD:獲取資源的元數據。
OPTIONS:獲取信息,關於資源的哪些屬性是客戶端能夠改變的。java

  • REST 成熟度模型。

該模型把 REST 服務按照成熟度劃分紅 4 個層次:git

  1. 第一個層次(Level 0)的 Web 服務只是使用 HTTP 做爲傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬於此類。
  2. 第二個層次(Level 1)的 Web 服務引入了資源的概念。每一個資源有對應的標識符和表達。
  3. 第三個層次(Level 2)的 Web 服務使用不一樣的 HTTP 方法來進行不一樣的操做,而且使用 HTTP 狀態碼來表示不一樣的結果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。
  4. 第四個層次(Level 3)的 Web 服務使用 HATEOAS。在資源的表達中包含了連接信息。客戶端能夠根據連接來發現能夠執行的動做。

RESTFUL URL命名原則

API URI 設計最重要的一個原則: nouns (not verbs!) ,名詞(而不是動詞)。github

CRUD 簡單例子:web

方法 URL 功能
GET /users 獲取用戶列表
GET /users/1 獲取 id 爲 1 的用戶
POST /users 建立一個用戶
PUT /users/1 替換 id 爲 1 的用戶
PATCH /users/1 修改 id 爲 1 的用戶
DELETE /users/1 刪除 id 爲 1 的用戶

上面是對某一種資源進行操做的 URI,那若是是有關聯的資源,或者稱爲級聯的資源,該如何設計 URI 呢?好比某一用戶下的產品:spring

方法 URL 功能
GET /users/1/products 獲取 Id 爲 1 用戶下的產品列表
GET /users/1/products/2 獲取 Id 爲 1 用戶下 Id 爲 2 的產品
POST /users/1/products 在 Id 爲 1 用戶下,建立一個產品
PUT /users/1/products/2 在 Id 爲 1 用戶下,替換 Id 爲 2 的產品
PATCH /users/1/products/2 修改 Id 爲 1 的用戶下 Id 爲 2 的產品
DELETE /users/1/products/2 刪除 Id 爲 1 的用戶下 Id 爲 2 的產品

HAL風格REST

HAL(Hypertxt Application Language)是一個被普遍採用的超文本表達的規範。應用能夠考慮遵循該規範。

規範事例(查詢列表)以下:sql

{
    "_links": {
        "self": {
            "href": "http://localhost:8080/lists"
        }
    }, 
    "_embedded": {
        "lists": [
            {
                "id": 1, 
                "name": "Default", 
                "_links": {
                    "todo:items": {
                        "href": "http://localhost:8080/lists/1/items"
                    }, 
                    "self": {
                        "href": "http://localhost:8080/lists/1"
                    }, 
                    "curies": [
                        {
                            "href": "http://www.midgetontoes.com/todolist/rels/{rel}", 
                            "name": "todo", 
                            "templated": true
                        }
                    ]
                }
            }
        ]
    }
}

目前github提供的api就是這種風格。在返回結果中添加額外的信息(連接)以後,服務器端提供的表達能夠幫助客戶端更好的發現服務器端所支持的動做。數據庫

在具體的表達中,應用雖然能夠根據須要選擇最適合的格式,可是在表達的基本結構上應該遵循必定的規範,這樣能夠保證最大程度的適用性。json

Spring Data REST簡介

什麼是Spring Data RESTapi

Spring Data REST是基於Spring Data的repository之上,能夠把 repository 自動輸出爲REST資源,目前支持Spring Data JPA、Spring Data MongoDB、Spring Data Neo4j、Spring Data GemFire、Spring Data Cassandra的 repository 自動轉換成REST服務,注意是自動。

簡單點說,Spring Data REST把咱們須要編寫的大量REST模版接口作了自動化實現,並符合HAL的規範.

實現

本例子使用Jpa來實現數據庫的操做,具體實現請點擊 例子倉庫查看

依賴

pom.xml加入

<!--Spring Data Rest-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<!--Spring Data Hal測試工具-->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-rest-hal-browser</artifactId>
</dependency>

開發

  • 定義實體類

Product.java

package com.springboot.services.producer.jpa.entity.po;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product extends JpaBasePo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column
    private String description;
}
  • 定義Dao類

ProductMapper.java

package com.springboot.services.producer.jpa.dao;

import com.springboot.services.producer.jpa.entity.po.Product;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource
public interface ProductMapper extends PagingAndSortingRepository<Product, Long> {
}

配置

spring:
  jpa:
    show-sql: true
    generate-ddl: true

就是這樣咱們就已經提供了一個關於prodcut的rest api,簡單的增、刪、改、查都有了。連Controller都不用寫,spring已經實現了。

測試

啓動服務

啓動命令:mvn springboot:run

  • api首頁:curl -i -X GET http://localhost:9001/

站點首頁列出了全部的api列表,目前一個products,爲http://localhost:9001/products{?page,size,sort}查詢產品列表的url,客戶端根據url導航就能夠進一步的調用api。

http://localhost:9001/profile能夠獲取api的原數據。

{
  "_links" : {
    "products" : {
      "href" : "http://localhost:9001/products{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:9001/profile"
    }
  }
}
  • 查詢列表:curl -i -X GET http://localhost:9001/products
{
  "_embedded" : {
    "products" : [ {
      "createdBy" : "system",
      "updatedBy" : "system",
      "createdTime" : "2018-07-22T23:40:38.288+0800",
      "updatedTime" : "2018-07-22T23:40:38.288+0800",
      "name" : "我是產品13111",
      "description" : "我是產品131的描述",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9001/products/1"
        },
        "product" : {
          "href" : "http://localhost:9001/products/1"
        }
      }
    },{}]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9001/products{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:9001/profile/products"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 16,
    "totalPages" : 1,
    "number" : 0
  }
}
  • 查詢單個實體:curl -i -X GET http://localhost:9001/products/1
{
  "createdBy" : "system",
  "updatedBy" : "system",
  "createdTime" : "2018-07-22T23:40:38.288+0800",
  "updatedTime" : "2018-07-22T23:40:38.288+0800",
  "name" : "我是產品13111",
  "description" : "我是產品131的描述",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9001/products/0"
    },
    "product" : {
      "href" : "http://localhost:9001/products/0"
    }
  }
}
  • 刪除:curl -i -X DELETE http://localhost:9001/products/0
  • 修改:curl -i -X PUT -H Content-Type:application/json -d '{"name":"產品1"}' http://localhost:9001/products/0
{
  "createdBy" : "system",
  "updatedBy" : "system",
  "createdTime" : "2018-08-11T19:29:45.755+0800",
  "updatedTime" : "2018-08-11T19:29:45.755+0800",
  "name" : "產品1",
  "description" : null,
  "_links" : {
    "self" : {
      "href" : "http://localhost:9001/products/0"
    },
    "product" : {
      "href" : "http://localhost:9001/products/0"
    }
  }
}
  • 新增:curl -i -X POST -H Content-Type:application/json -d '{"name":"產品1"}' http://localhost:9001/products
{
  "createdBy" : "system",
  "updatedBy" : "system",
  "createdTime" : "2018-08-11T19:24:33.072+0800",
  "updatedTime" : "2018-08-11T19:24:33.072+0800",
  "name" : "產品1",
  "description" : "desc",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9001/products/10"
    },
    "product" : {
      "href" : "http://localhost:9001/products/10"
    }
  }
}

啓動瀏覽器訪問

url:http://localhost:9001/browser/index.html#/

clipboard.png

相關文章
相關標籤/搜索