【Java开发】 Spring 09 :Spring Data REST 实现并访问简单的超媒体服务

Spring Data REST 是提供一个灵活和可配置的机制来编写可以通过HTTP公开的简单服务,简单来说,而且可以省去大部分controller和services的逻辑,因为Spring Data REST 已经为你都做好了,目前支持JPA、MongoDB、Neo4j、Solr、Cassandra 和 Gemfire,因此本文以 JPA 举例。

目录

1 Spring Data REST 介绍

1.1 RestFul

1.2 HATEOAS

1.3 Spring Data REST

2 项目初始化

2.1 通过 IDEA Spring Initializr 创建项目

2.2 添加 Spring Web 等依赖

2.3 连接数据库配置

2.4 编写数据库对应的实体类

2 REST 资源(超链接服务)的实现

2.1 添加 data-rest 依赖

2.2 编写 UserRepository 接口

2.3 REST 相关页面

① 根路径:http://localhost:8080/

 ② 分页查询:http://localhost:8080/user?page=1&size=2

③ 基路径:http://localhost:8080/user

④ 查询某个user :http://localhost:8080/user/1

⑤ 自定义方法展示:http://localhost:8080/user/search

2.4 对 REST 资源进行 Post 请求

① 新增--POST  http://127.0.0.1:8080/user

② 修改--PUT  http://127.0.0.1:8080/userses/2

3 使用 restTemplate 访问超链接服务

3.1 HAL 渲染配置

3.2 get 请求

① getLink 获取 Linkl

② 分页查询

③ 查询所有 User

④ 查询某个 User

3.2 post 请求


1 Spring Data REST 介绍

Spring Data REST 的发展是经历了好一些阶段的,主要包括以下概念 👇

1.1 RestFul

REST 这个词是2000 年 Roy Fielding 在其博士论文中创造出来的,它是一种架构风格,它包含了一个分布式超文本系统中对于组件、连接器和数据的约束。简单来说,REST 是对互联网资源接口的一种约束标准,类似于食品质量标准。

REST 是作为互联网自身架构的抽象而出现的,其关键在于所定义的架构上的各种约束。只有满足这些约束,才能称之为符合 REST 架构风格,即所谓的“RESTful”服务。

1.2 HATEOAS

HATEOAS(Hypermedia as the engine of application state)是 REST 架构风格中最复杂的约束,也是构建成熟 REST 服务的核心。它的重要性在于打破了客户端和服务器之间严格的契约,使得客户端可以更加智能和自适应,而 REST 服务本身的演化和更新也变得更加容易。

简单来说,这里的自适应,指得是服务器返回给客户端的信息中,会自动包含客户端能够进行的操作,这样二者之间就不用通过建立一份额外的文档来约定访问的格式了。

Spring Hateoas,是Spring的一个子项目,Spring HATEOAS 的主要功能在于提供了简单的机制来创建这些满足 HATEOAS 要求的链接,并与 Spring MVC 框架有很好的结合。不过在实践中,Spring Data REST 更为常用。

1.3 Spring Data REST

Spring Data Rest 也是 Spring 的一个子项目,它的主要功能就是把你的 Spring Data Repositories 以满足 HATEOAS 的格式暴露出去,因此你的 JPA 资源 Repository 可以用一种通用的 http 访问格式对你的 Respository 进行 CRUD ,而且可以省去大部分 controller 和 services 的逻辑,因为Spring Data REST 已经为你都做好了,你只需要保证你的http访问格式正确即可!

官方文档Spring Data REST Reference Documentation

目前 Spring Data REST 支持以下各datasource的的 repository 自动转换成 REST 服务

  • Spring Data JPA
  • Spring Data MongoDB
  • Spring Data Neo4j
  • Spring Data GemFire

简单来说,Spring Data REST 把我们需要编写的大量 REST 模版接口做了自动化实现。

2 项目初始化

项目初始化前提是已构建实体类对应的数据库,关于数据库的初始化详见【Spring 06】的 2.1 小节,当然对初始化的步骤熟悉的同学可直接跳过,直接从第 3 章开始了解即可~

2.1 通过 IDEA Spring Initializr 创建项目

2.2 添加 Spring Web 等依赖

2.3 连接数据库配置

路径:src/main/resources/application.properties

#数据库连接配置
spring.datasource.username=root
spring.datasource.password=root
#mysql5~8 驱动不同driver-class-name     8需要增加时区的配置serverTimezone=UTC,放在url最后
#useSSL=false 安全连接
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

2.4 编写数据库对应的实体类

@Entity 注释比较关键,没了它不行

路径:src/main/java/com/restdemo/pojo/User.java

package com.restdemo.pojo;

import lombok.*;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import java.io.Serializable;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

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

    private String name;
    private Integer age;
    private String email;

}

以上准备工作准备完毕,不需要像 MVC 项目那样编写 Service层、Controller层等内容~

2 REST 资源(超链接服务)的实现

2.1 添加 data-rest 依赖

路径:pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>

2.2 编写 UserRepository 接口

路径:src/main/java/com/restdemo/repository/UserRepository.java

package com.restdemo.repository;

import com.restdemo.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import java.util.List;

@RepositoryRestResource( path = "user") //指定基路径
public interface UserRepository extends JpaRepository<User,Long> {

    //自定义方法,JpaRepository提供了很多供使用
    User findUserByName(@Param("name") String name);//参数可以用 @Param 自定义注解

    //根据name查询,查询到的结果根据Id排序
    List<User> findByNameInOrderById(List<String> list);
}

2.3 REST 相关页面

① 根路径:http://localhost:8080/

 ② 分页查询:http://localhost:8080/user?page=1&size=2

③ 基路径:http://localhost:8080/user

返回所有的 user 记录

④ 查询某个user :http://localhost:8080/user/1

查询 id 为 1 的 user

⑤ 自定义方法展示:http://localhost:8080/user/search

展示自定义的方法~

使用其中的第二个方法 👇

2.4 对 REST 资源进行 Post 请求

此时用到辅助工具 Apifox(类似Postman)👇,请求参数与属性名相同自动填充到 user 对象中

① 新增--POST  http://127.0.0.1:8080/user

 返回内容 👇

数据库新增成功 👇

② 修改--PUT  http://127.0.0.1:8080/userses/2

返回内容 👇

数据库修改成功 👇

3 使用 restTemplate 访问超链接服务

注意由于是请求本地项目的 web 资源,因此需要在先启动项目 👇,然后再对测试类进行测试

关于 restTemplate 的简单教学在该链接:Spring 08 :访问 Web 资源,首先浅浅地用下 restTemplate 👇

package com.restdemo;

@SpringBootTest
class RestdemoApplicationTests {

    //new 一个RestTemplate ,后面也会用到
    RestTemplate restTemplate = new RestTemplate();

    @Test
    public void queryOneUser(){
        //构建 uri
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:8080/user")
                .build()
                .toUri();
        //执行rest请求,ResponseEntity封装了返回信息,若将getForEntity 替换成 getForObject,则不需要 ResponseEntity
        ResponseEntity<Object> user = restTemplate.getForEntity(uri,Object.class);
        //打印返回信息
        System.out.printf("Response Status: {%s}"+"\n"+"Response Headers: {%s}"+"\n", user.getStatusCode(), user.getHeaders().toString());
        System.out.printf("Users: {%s}", user.getBody());
    }
}

此时返回是不带任何处理的,所有的记录及 links 都混在一起,那么就需要其他办法处理分析返回的数据!

3.1 HAL 渲染配置

路径:src/main/java/com/restdemo/RestdemoApplication.java

package com.restdemo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

@SpringBootApplication
@EnableHypermediaSupport(type = {EnableHypermediaSupport.HypermediaType.HAL}) //注入HAL渲染
public class RestdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(RestdemoApplication.class, args);
    }

    //注册Jackson2HalModule 开启Jackson JSON对HAL的支持
    @Bean
    public Jackson2HalModule jackson2HalModule() {
        return new Jackson2HalModule();
    }

    @Bean
    public RestTemplate restTemplate() {
        final ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Jackson2HalModule());

        final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
        converter.setObjectMapper(mapper);

        return new RestTemplate(Collections.<HttpMessageConverter<?>>singletonList(converter));
    }
}

3.2 get 请求

路径:src/test/java/com/restdemo/RestdemoApplicationTests.java

① getLink 获取 Linkl

对 getLink 方法进行测试,主要是获取对应的 Link 供后边使用~

@SpringBootTest
class RestdemoApplicationTests {

    @Autowired
    private RestTemplate restTemplate;

    //构建 URI
    private static final URI ROOT_URI = URI.create("http://localhost:8080");
    //输出的links:{<http://localhost:8080/user{?page,size,sort}>;rel="users",<http://localhost:8080/profile>;rel="profile"}
    private static final URI SEARCH_URI = URI.create("http://localhost:8080/user/search");
    //输出的links:{<http://localhost:8080/user/search/findByNameInOrderById{?list}>;rel="findByNameInOrderById",<http://localhost:8080/user/search/findUserByName{?name}>;rel="findUserByName",<http://localhost:8080/user/search>;rel="self"}

    @Test
    public void mainTest(){
        Link link = getLink(ROOT_URI,"users");
    }

    //我们根据 ROOT_URI or SEARCH_URI 返回的 Links 进行相应的请求或处理
    public Link getLink(URI uri, String rel){
        //返回 link 的包装数据
        ResponseEntity<CollectionModel<Links>> rootResp =
                restTemplate.exchange(uri, HttpMethod.GET, null,
                        new ParameterizedTypeReference<CollectionModel<Links>>() {});
        //从包装数据中提取对应的 link
        Link link = rootResp.getBody().getRequiredLink(rel);
        System.out.printf("根据{%s},查找到 Link:{%s}", rel,link);
        return link;
    }

输出如下 👇,获取到预期的 Link

② 分页查询

此处就要用到 getLink 方法了!

    @Test
    public void queryPage(){
        //获取 link 模板
        Link link = getLink(ROOT_URI,"users");
        //page=1,size=2,sort="id",进行分页查询
        ResponseEntity<CollectionModel<Object>> usersResp =
                restTemplate.exchange(link.getTemplate().expand(1,2,"id"), HttpMethod.GET, null,
                        new ParameterizedTypeReference<CollectionModel<Object>>() {});
        //打印返回信息
        System.out.printf("\n"+"分页查询结果: {%s}", usersResp.getBody().getContent());
    }

输出成功 👇

③ 查询所有 User

    @Test
    public void queryAllUsers(){
        //基路径
        URI ROOT_URI1 = URI.create("http://localhost:8080/user");
        //执行 get 请求
        ResponseEntity<CollectionModel<User>> rootResp =
                restTemplate.exchange(ROOT_URI1, HttpMethod.GET, null,
                        new ParameterizedTypeReference<CollectionModel<User>>() {});
        //打印返回信息,若User实体放里边,返回会带id且为null,个人认为不是很美观,所以后边均为Object
        System.out.printf("All users: {%s}", rootResp.getBody().getContent());
    }

输出成功 👇

④ 查询某个 User

    @Test
    public void queryOneUser(){
        //获取 link 模板
        Link link = getLink(SEARCH_URI,"findUserByName");
        //执行 get 请求
        ResponseEntity<EntityModel<Object>> usersResp =
                restTemplate.exchange(link.getTemplate().expand("yinyu"), HttpMethod.GET, null,
                        new ParameterizedTypeReference<EntityModel<Object>>() {});
        //打印返回信息
        System.out.printf("\n"+"The user: : {%s}", usersResp.getBody());
    }

输出成功 👇

3.2 post 请求

    @Test
    public void postAddUser(){
        //构建uri
        URI uri = URI.create("http://localhost:8080/user");
        //请求数据包装
        User user = User.builder().name("yinyu222").age(18).email("yinyu@163.com").build();
        RequestEntity<User> req = RequestEntity.post(uri).body(user);
        //post请求执行
        ResponseEntity<EntityModel<Object>> resp =
                restTemplate.exchange(req,
                        new ParameterizedTypeReference<EntityModel<Object>>() {});
        //打印返回信息
        System.out.printf("Response messae: : {%s}", resp.getBody());
    }

返回成功 👇

 


参考文章

SpringBoot实战之使用 Spring Data REST 实现简单的超媒体服务 | 码农家园

Spring Data REST - 慕尘 - 博客园

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尹煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值