使用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

相关文章
相关标签/搜索