Spring Data REST

Posted 逆水行舟,不进则退

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Data REST相关的知识,希望对你有一定的参考价值。

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;
}
View Code

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> {
}
View Code

这样就已经提供了一个关于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,则返回空白

 

 

 

 

 

 

 

以上是关于Spring Data REST的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data REST

Spring Data REST API集成SpringfoxSwagger

Jason中的Spring Data Rest -Disable自我链接(HAL)

在Spring Boot Data JPA Rest API中使用PostgreSql时,表未找到错误

Spring Data Rest 2.0.0.RELEASE 打破了以前使用 RC1 的代码

Spring Data Rest - 在 Json 中禁用自我链接(HAL)