本篇来学习RESTful相关知识,相信通过本篇的学习,开发者就能在短时间内开发出一套属于自己的RESTful风格的应用。

REST简介

REST(Representational State Transfer )是一种Web软件架构风格,它是一种风格,而不是标准,匹配或兼容这种架构风格的网络服务称为REST服务。REST服务简洁并且有层次,REST通常基于HTTP、URI和XML以及HTML这些现有的广泛流行的协议和标准。在REST中,资源是由URI 来指定的,对资源的增删改查操作可以通过HTTP协议提供的GET、POST、PUT、DELETE等方法实现。使用REST可以更高效地利用缓存来提高响应速度, 同时REST中的通信会话状态由客户端来维护,这可以让不同的服务器处理一系列请求中的不同请求,进而提高服务器的扩展性。在前后端分离项目中,一个设计良好的Web软件架构必然要满足RESTful风格。
在Spring MVC框架中,开发者可以通过@RestController注解开发一个REST服务,不过,Spring Boot对此提供了自动化配置方案,开发者只需要添加相关依赖就能快速构建一个RESTful服务。

JPA实现REST

在SpringBoot中,使用Spring Data JPASpring Data Rest即可快速开发出一个RESTful风格的应用。接下来向读者介绍SpringBoot中非常方便的RESTful应用开发。

基本实现

第一步,创建项目。使用spring Initializr构建工具构建一个SpringBoot的Web应用,名称为restfulspringboot,然后在pom.xml文件中添加如下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入data-jpa依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--引入data-rest依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<!--引入数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--引入druid数据库连接-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.25</version>
</dependency>
<!--引入lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

这里的依赖除了数据库相关的依赖外,还有Spring Data JPA的依赖和Spring Data Rest的依赖,别忘了还有lombok依赖。项目创建完成后,需要在application.properties配置文件中配置基本的数据库连接信息:

1
2
3
4
5
6
7
8
9
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jparestful?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=1234

spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect

上面这些都是数据库和JPA最基本的配置,因此配置的含义就不再介绍了,前面文章都有记录的。

第二步,创建实体类。新建pojo包,并在其中创建Book实体类,其中的代码为:

1
2
3
4
5
6
7
8
9
@Entity(name = "t_book")
@Data
public class Book {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
private String author;
}

第三步,创建BookRepository接口。新建repository包,并在其中创建BookRepository接口,并继承JpaRepository接口,其中的代码为:

1
2
public interface BookRepository extends JpaRepository<Book,Integer> {
}

只需要创建BookRepository接口,并继承JpaRepository接口即可,对数据表基本的增删改查操作这个JpaRepository接口已经实现了,不用再定义。如果其中的方法不满足需求,可以按照自己的需求来进行自定义扩展。需要注意的是JPA新增和修改方法都是save。

第四步,测试。经过前面三步的配置,一个RESTful服务就构建成功了,可能有些童鞋会问“这里什么都没写呀,controller都没有怎么实现方法的请求调用?”是的,这就是SpringBoot的魅力所在。
RESTful的测试首先需要有一个测试工具,这里推荐使用Postman测试工具。
第五步,添加测试。RESTful服务构建成功后,默认的请求路径是实体类名小写再加上后缀s。此时向数据库中添加一条数据是非常融合用的操作,只需要发起一个post请求,请求地址为http://localhost:8080/books,如下图所示:

当添加成功后,服务端会返回刚刚添加成功的数据的基本信息以及浏览地址。
第六步,查询测试。查询是使用GET请求,分页查询请求路径为/books,自然请求的URL地址为http://localhost:8080/books,分页查询请求默认每页记录数为20条,页数为0(页码从0开始计算),查询结果如下如所示:

当然了,前面在添加数据时,当数据添加成功时就返回了该条数据的访问地址,因此如果你想按照id来进行查询,只需要在/books后面追加上id即可,如查询id为1的book,则使用的URL连接为:
http://localhost:8080/books/1,访问结果如下所示:

不知道你发现没有在上面分页查询的结果中不仅包含了所有图书的基本信息,还包含了如何发起一个分页请求,以及当前页面的分页信息:

如果开发者想要修改请求页码和每页记录数,只需要在请求地址栏中携带上相关参数即可,举个例子来说如下请求表示查询第2页数据(用户看到的页数从1计算,开发人员的页数从0计算),且每页记录数为3:

1
http://localhost:8080/books?page=1&size=3

除了分页外,默认还支持排序,如想查询第2页数据(用户看到的页数从1计算,开发人员的页数从0计算),每页记录数为3,且按照id进行倒序排列,此时使用的请求地址为:

1
http://localhost:8080/books?page=1&size=3&sort=id,desc

第七步,修改测试。修改测试使用PUT方式,因此只需要发送PUT请求即可实现修改数据,注意对数据的修改是通过id来实现的,因此请求路径中要有id,例如如下请求路径表示修改id为2的记录,具体的修改内容在请求体中,如下所示:

1
http://localhost:8080/books/2

PUT请求的返回结果就是被修改之后的记录。

第八步,删除测试。删除测试使用DELETE方式,因此只需要发送DELETE请求即可实现删除数据,同样删除数据需要传id,举个例子删除id为4的记录的请求如下:

1
http://localhost:8080/books/4

请注意DELETE请求是没有返回值的,上面这个请求发送成功后,id为4的记录就被删除了:

自定义请求路径

通过前面的学习,你已经知道默认的请求路径是实体类名小写再加上后缀s,这可能不符合公司对于API命名的规范,因此作为开发者肯定是想对请求路径进行重定义的,只需要通过添加@RepositoryRestResource注解即可实现。举个例子,前面的例子就只需要在BookRepository接口上添加@RepositoryRestResource注解即可实现请求路径的自定义,相应的代码为:

1
2
3
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book,Integer> {
}

简单解释一下上述注解中各个参数的含义:path参数表是所有请求路径中的books都修改为了bs,像http://localhost:8080/bs等;collectionResourceRel顾名思义就是集合资源参考值,表示将返回的JSON集合中book集合的key都修改为bs;itemResourceRel表示将返回的JSON集合中的单个book的key修改为b,如下图所示:

自定义查询方法

既然可以自定义访问路径,那么也是可以自定义查询方法的。默认的查询方法支持分页查询、排序查询以及按照id查询,如果开发者想要按照某个属性查询,只需要在BookRepository接口中定义相关的方法并暴露出去即可,相应的代码为:

1
2
3
4
5
6
7
8
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book,Integer> {
@RestResource(path = "author",rel = "author")
List<Book> findByAuthorContains(@Param("author")String author);

@RestResource(path = "name",rel = "name")
Book findByNameEquals(@Param("name")String name);
}

简单解释一下上述代码的含义:自定义查询只需要在BookRepository中定义相关查询方法即可,方法定义好之后可以不添加@RestResource注解,默认路径就是方法名。以List<Book> findByAuthorContains(@Param("author")String author);这行代码为例,如果不添加@RestResource注解则默认该方法的调用路径为http://localhost:8080/bs/search/findByAuthorContains?author=鲁迅。如果想对查询路径进行自定义,只需要添加@RestResource注解即可。path属性表示最新的路径,还是以前面的findByAuthorContains方法为例,添加@RestResource(path = "author",rel = "author")注解后的查询路径为http://localhost:8080/bs/search/author?author=鲁迅

用户可直接访问http://localhost:8080/bs/search路径查看该实体类暴露出来了哪些查询方法,默认情况下,在查询方法展示时使用的路径是方法名,通过@RestResource注解中的rel属性可以对这里的路径进行重定义,如下图所示:

注意@RestResource注解中的path也必须有,不能仅仅设置rel属性。

隐藏方法

默认情况下,凡是继承了Repository接口(或者Repository的子类)的类都会被暴露出来,即开发者可执行基本的增删改查方法。以上文的BookRepository接口为例,如果开发者提供了BookRepository继承自Repository,就能执行对Book的基本操作,如果开发者继承了Repository但是又不想暴露相关操作,只需做如下配置即可:

1
2
3
4
@RepositoryRestResource(exported = false)
public interface BookRepository extends JpaRepository<Book,Integer> {

}

@RepositoryRestResource注解中的exported属性设置为false之后,则不仅JpaRepository中定义增删改查接口会失效,而且BookRepository类中定义的相关方法也会失效。如果开发者只想单纯的不想暴露某个方法,只需要在相应的方法上配置即可。举个例子开发者想屏蔽DELETE接口,只需要做如下配置:

1
2
3
4
5
6
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book,Integer> {
@Override
@RestResource(exported = false)
void deleteById(Integer id);
}

请注意@RestResource注解的exported属性默认为true,只需将其设置为false即可。

配置CORS

在前面介绍了CORS两种不同的配置方式,一种是直接在方法上添加@CorssOrigin注解,另一种是全局配置。全局配置在这里依然适用,但是默认的RESTful工程不需要开发者自己提供Controller,因此添加在Controller的方法上的注解可以直接写在BookRepository上,相应的代码为:

1
2
3
4
5
6
7
8
9
@CrossOrigin
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book,Integer> {
@RestResource(path = "author",rel = "author")
List<Book> findByAuthorContains(@Param("author")String author);

@RestResource(path = "name",rel = "name")
Book findByNameEquals(@Param("name")String name);
}

此时BookRepository接口中的所有方法都支持跨域。如果开发者只想让某一个方法支持跨域,那么只需将@CrossOrigin注解添加到某一个方法上即可。

其他配置

开发者也可以在application.properties配置文件中配置一些常用的属性,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 每页默认记录数,默认值为20,此处设置为2
spring.data.rest.default-page-size=2
# 分页查询页码参数名,默认值为page
spring.data.rest.page-param-name=page
# 分页查询记录数参数名,默认值为size
spring.data.rest.limit-param-name=size
# 分页查询排序参数名,默认值为sort
spring.data.rest.sort-param-name=sort
# base-path表示给所有请求路径上添加前缀
spring.data.rest.base-path=/api
# 添加成功时是否返回添加内容
spring.data.rest.return-body-on-create=true
# 更新成功时是否返回更新内容
spring.data.rest.return-body-on-update=true

当然这些XML配置也可以在Java代码中配置,且代码中配置的优先级高于application.properties配置文件。新建config包,并在其中新建RestConfig类,注意它需要继承RepositoryRestConfigurerAdapter类(新版请使用SpringBootRepositoryRestConfigurer来代替)

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class RestConfig extends RepositoryRestConfigurerAdapter {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultPageSize(2)
.setPageParamName("page")
.setLimitParamName("size")
.setSortParamName("sort")
.setBasePath("/api")
.setReturnBodyOnCreate(true)
.setReturnBodyOnUpdate(true);
}
}

可以看到这里的配置和之前使用application.properties配置文件设置的完全一致,因此就不过多介绍了。

MongoDB实现REST

前面学习了SpringBoot整合MongoDB,而使用SpringBoot快速构建RESTful服务除了结合Spring Data JPA以外,也可以结合Spring Data MongoDB来实现。使用Spring Data MongoDB构建RESTful服务也是三个步骤:

第一步,创建项目。*使用spring Initializr构建工具构建一个SpringBoot的Web应用,名称为mongodbrestfulspringboot,然后在pom.xml文件中添加如下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入data-mongodb依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!--引入data-rest依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<!--引入lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

这里的除了Spring Data MongoDB的依赖和Spring Data Rest的依赖,别忘了还有lombok依赖。项目创建完成后,需要在application.properties配置文件中配置基本的数据库连接信息:

1
2
3
4
5
6
7
8
9
10
11
12
# 验证信息登录库
spring.data.mongodb.authentication-database=admin
# 要连接的库,认证信息不一定要在连接的库中创建,因此这两个分开配置
spring.data.mongodb.database=test
# mongodb的主机IP
spring.data.mongodb.host=192.168.2.132
# mongodb的端口
spring.data.mongodb.port=27017
# mongodb的用户名
spring.data.mongodb.username=envy
# mongodb的密码
spring.data.mongodb.password=123

上面这些都是MongoDB数据库最基本的配置,因此配置的含义就不再介绍了,前面文章都有记录的。

第二步,创建实体类。新建pojo包,并在其中创建Book实体类,其中的代码为:

1
2
3
4
5
6
@Data
public class Book {
private Integer id;
private String name;
private String author;
}

第三步,创建BookRepository接口。新建repository包,并在其中创建BookRepository接口,并继承MongoRepository接口,其中的代码为:

1
2
public interface BookRepository extends MongoRepository<Book,Integer> {
}

第四步,测试。请注意在启动SpringBoot项目之前,先要启动MongoDB。SpringBoot项目启动后,接下来的测试步骤和前面Spring Data JPA中的步骤完全一致,因此就忽略了,且Spring Data Rest相关配置在这里也是同样适用的。

构建RESTful服务小结

本篇学习了SpringBoot构建RESTful服务,结合Spring Data Rest Spring Data JP A以及Spring Data MongoDB,Spring Boot可以快速构建出一个基本的RESTful服务,而开发者可以结合具体情况选择关系型数据库或者非关系型数据库作为数据支撑。在一些常规功能的项目中,Spring Boot的这些特性可以帮助开发者省去许多繁杂臃肿的配置,所以使用SpringBoot构建RESTful服务是一个非常不错的选择。