在本 Spring Boot 教程中,我将向您展示一个 Restful Web 服务示例,其中 Spring REST 控制器可以接收/使用 XML 请求正文并返回 XML 响应而不是 JSON。我们还使用 Spring Data JPA 与数据库(MySQL/PostgreSQL)进行交互。
更多实践:
– Spring Boot、Spring Data JPA – 构建 Rest CRUD API 示例
– Spring Boot + GraphQL + MySQL 示例
部署:
–在 AWS 上部署 Spring Boot 应用程序 – Elastic Beanstalk
– Docker Compose:Spring Boot 和 MySQL 示例
内容[隐藏]
Spring Boot XML REST 服务
呈现 XML 响应的方法有两种:
使用 Jackson XML 扩展 ( jackson-dataformat-xml
) 来呈现 XML 响应很容易,只需添加以下依赖项:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
使用具有依赖关系的 JAXB:
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
所以 XML 可以通过@XmlRootElement
在数据模型 lass 处注解来渲染:
@XmlRootElement
public class Tutorial {
private String title;
// .. getters and setters
}
在这个例子中,我们将使用第一种方式。
返回 XML 响应
MediaType.APPLICATION_XML_VALUE
您可以通过使用作为注释的produces
值来告诉控制器哪些方法应该返回 XML 响应。@RequestMapping
@RequestMapping(value="/tutorials/{id}", produces=MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") long id) {
// ...
}
如果您想让所有 Controller 方法都返回 XML 响应,则不必用produces=MediaType.APPLICATION_XML_VALUE
. 只需通过实施WebMvcConfigurer
和覆盖来实施内容协商configureContentNegotiation()
。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_XML);
}
}
Spring Boot Rest XML 示例概述
我们将构建一个为教程应用程序提供 CRUD API 的 Restful Web 服务:
- 每个教程都有 ID、标题、描述、发布状态。
- API 帮助创建、检索、更新、删除教程。
- APIs 还支持自定义查找器方法,例如按已发布状态或按标题查找。
- XML 是 HTTP 请求和响应的内容类型。
以下是 API:
方法 | 网址 | 行动 |
---|---|---|
邮政 | /api/教程 | 创建新教程 |
得到 | /api/教程 | 检索所有教程 |
得到 | /api/tutorials/:id | 检索教程:id |
放 | /api/tutorials/:id | 更新教程:id |
删除 | /api/tutorials/:id | 删除教程:id |
删除 | /api/教程 | 删除所有教程 |
得到 | /api/教程/已发布 | 查找所有已发布的教程 |
得到 | /api/tutorials?title=[关键字] | 查找标题包含的所有教程keyword |
– 我们使用 Spring Data JPA 的 CRUD 操作和查找方法JpaRepository
。
– 根据我们配置项目依赖和数据源的方式,数据库可以是 PostgreSQL 或 MySQL。
技术
- 爪哇 8
- Spring Boot 2.2.1(带有 Spring Web MVC、Spring Data JPA)
- PostgreSQL/MySQL
- Maven 3.6.1
- 杰克逊数据格式 XML 2.10.1
项目结构
–WebConfig
实现WebMvcConfigurer。这是我们设置默认内容类型的地方。
–Tutorial
数据模型类对应实体和表教程。
–是为 CRUD 方法和自定义查找器方法TutorialRepository
扩展JpaRepository的接口。它将自动装配到TutorialController
.
–TutorialController
是一个RestController,它具有 RESTful 请求的请求映射方法,例如:getAllTutorials、createTutorial、updateTutorial、deleteTutorial、findByPublished …
– Spring Datasource、JPA 和 Hibernate 的配置应用程序.properties。
– pom.xml包含 Spring Boot、ackson Dataformat XML 和 MySQL/PostgreSQL 的依赖项。
创建和设置 Spring Boot 项目
使用Spring Web 工具或您的开发工具(Spring Tool Suite、Eclipse、Intellij)创建一个 Spring Boot 项目。
然后打开pom.xml并添加这些依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
我们还需要再添加一个依赖项。
– 如果你想使用MySQL:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
– 或PostgreSQL:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
配置 Spring Datasource、JPA、Hibernate
在 src/main/resources 文件夹下,打开 application.properties 并写入这些行。
– 对于 MySQL:
spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false
spring.datasource.username= root
spring.datasource.password= 123456
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update
– 对于 PostgreSQL:
spring.datasource.url= jdbc:postgresql://localhost:5432/testdb
spring.datasource.username= postgres
spring.datasource.password= 123
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update
spring.datasource.username
&spring.datasource.password
属性与您的数据库安装相同。- Spring Boot 使用 Hibernate 进行 JPA 实现,我们
MySQL5InnoDBDialect
为 MySQL 或PostgreSQLDialect
PostgreSQL配置 spring.jpa.hibernate.ddl-auto
用于数据库初始化。我们将 value 设置为 value,update
以便在数据库中创建一个表,自动对应于定义的数据模型。对模型的任何更改也将触发对表的更新。对于生产,这个属性应该是validate
。
定义数据模型
我们的数据模型是具有四个字段的教程:id、title、description、published。
在模型包中,我们定义了Tutorial
类。
model/Tutorial.java
package com.bezkoder.spring.datajpa.xml.model;
import javax.persistence.*;
@Entity
@Table(name = "tutorials")
public class Tutorial {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "title")
private String title;
@Column(name = "description")
private String description;
@Column(name = "isPublished")
private boolean published;
public Tutorial() {
}
public Tutorial(String title, String description, boolean published) {
this.title = title;
this.description = description;
this.published = published;
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isPublished() {
return published;
}
public void setPublished(boolean isPublished) {
this.published = isPublished;
}
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
}
}
–@Entity
注解表明该类是一个持久的 Java 类。
–@Table
注释提供映射该实体的表。
–@Id
注释用于主键。
–@GeneratedValue
注解用于定义主键的生成策略。GenerationType.AUTO
表示自动增量字段。
–@Column
注释用于定义数据库中映射注释字段的列。
创建存储库接口
让我们创建一个存储库以与数据库中的教程进行交互。
在存储库包中,创建TutorialRepository
扩展接口JpaRepository
。
repository/TutorialRepository.java
package com.bezkoder.spring.datajpa.xml.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bezkoder.spring.datajpa.xml.model.Tutorial;
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
List<Tutorial> findByPublished(boolean published);
List<Tutorial> findByTitleContaining(String title);
}
现在我们可以使用 JpaRepository 的方法:save()
, findOne()
, findById()
, findAll()
, count()
, delete()
, deleteById()
... 而不实现这些方法。
我们还定义了自定义查找器方法:
– findByPublished()
:返回所有published
具有输入值的教程published
。
- findByTitleContaining()
:返回所有标题包含输入的教程title
。
该实现由Spring Data JPA自动插入。
为 XML 类型配置内容协商
现在我们要在我们的 Spring Boot 项目中实现内容协商。
config/WebConfig.java
package com.bezkoder.spring.datajpa.xml.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_XML);
}
}
创建 Spring Rest API 控制器
最后,我们创建一个控制器,提供用于创建、检索、更新、删除和查找教程的 API。
controller/TutorialController.java
package com.bezkoder.spring.datajpa.xml.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.bezkoder.spring.datajpa.xml.model.Tutorial;
import com.bezkoder.spring.datajpa.xml.repository.TutorialRepository;
@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {
@Autowired
TutorialRepository tutorialRepository;
@GetMapping("/tutorials")
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
try {
List<Tutorial> tutorials = new ArrayList<Tutorial>();
if (title == null)
tutorialRepository.findAll().forEach(tutorials::add);
else
tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(tutorials, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") long id) {
Optional<Tutorial> tutorialData = tutorialRepository.findById(id);
if (tutorialData.isPresent()) {
return new ResponseEntity<>(tutorialData.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@PostMapping("/tutorials")
public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
try {
Tutorial _tutorial = tutorialRepository.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED);
}
}
@PutMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
Optional<Tutorial> tutorialData = tutorialRepository.findById(id);
if (tutorialData.isPresent()) {
Tutorial _tutorial = tutorialData.get();
_tutorial.setTitle(tutorial.getTitle());
_tutorial.setDescription(tutorial.getDescription());
_tutorial.setPublished(tutorial.isPublished());
return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@DeleteMapping("/tutorials/{id}")
public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") long id) {
try {
tutorialRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED);
}
}
@DeleteMapping("/tutorials")
public ResponseEntity<HttpStatus> deleteAllTutorials() {
try {
tutorialRepository.deleteAll();
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED);
}
}
@GetMapping("/tutorials/published")
public ResponseEntity<List<Tutorial>> findByPublished() {
try {
List<Tutorial> tutorials = tutorialRepository.findByPublished(true);
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(tutorials, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED);
}
}
}
–@CrossOrigin
用于配置允许的来源。
–@RestController
注释用于定义控制器并指示方法的返回值应绑定到 Web 响应体。
–@RequestMapping("/api")
声明控制器中所有 APIs 的 url 都以/api
.
– 我们使用@Autowired
注入TutorialRepository
bean 到局部变量。
运行和测试
使用命令运行 Spring Boot 应用程序:mvn spring-boot:run
。
教程表将在数据库中自动生成。
例如,如果您检查 MySQL,您会看到如下内容:
mysql> describe tutorials;
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id | bigint(20) | NO | PRI | NULL | |
| description | varchar(255) | YES | | NULL | |
| is_published | bit(1) | YES | | NULL | |
| title | varchar(255) | YES | | NULL | |
+--------------+--------------+------+-----+---------+-------+
创建教程:
mysql> select * from tutorials;
+----+-----------------------+--------------+--------+
| id | description | is_published | title |
+----+-----------------------+--------------+--------+
| 1 | Tut#1 Description | 0 | Tut #1 |
| 2 | Tut#2 Description | 0 | Tut #2 |
| 3 | Tut#3 Description | 0 | Tut #3 |
| 4 | Description for Tut#4 | 0 | Tut #4 |
| 5 | Description for Tut#5 | 0 | Tut #5 |
+----+-----------------------+--------------+--------+
获取所有教程:
按 ID 获取教程:
更新一些教程:
mysql> select * from tutorials;
+----+-----------------------+--------------+---------------------+
| id | description | is_published | title |
+----+-----------------------+--------------+---------------------+
| 1 | Tut#1 Desc | 0 | bezkoder.com Tut #1 |
| 2 | Tut#2 Description | 0 | Tut #2 |
| 3 | Tut#3 Desc | 1 | bezkoder.com Tut #3 |
| 4 | Description for Tut#4 | 0 | Tut #4 |
| 5 | Tut#5 Desc | 1 | bezkoder.com Tut #5 |
+----+-----------------------+--------------+---------------------+
查找所有已发布的教程:
查找标题包含“zkoder”的所有教程:
删除教程:
mysql> select * from tutorials;
+----+-------------------+--------------+---------------------+
| id | description | is_published | title |
+----+-------------------+--------------+---------------------+
| 1 | Tut#1 Desc | 0 | bezkoder.com Tut #1 |
| 2 | Tut#2 Description | 0 | Tut #2 |
| 3 | Tut#3 Desc | 1 | bezkoder.com Tut #3 |
| 5 | Tut#5 Desc | 1 | bezkoder.com Tut #5 |
+----+-------------------+--------------+---------------------+
使用 API 删除所有教程:DELETE http://localhost:8080/api/tutorials/
mysql> select * from tutorials;
Empty set (0.00 sec)
结论
今天我们构建了一个 Spring Boot Rest XML 示例,使用 Jackson XML 扩展来渲染 XML 和 Spring Data JPA 以与 MySQL/PostgreSQL 交互。您已经知道在请求正文中使用 XML 并返回 XML 响应的方法。
如果您想了解有关在请求和响应中处理 JSON 数据的 Spring Boot Webservice 的更多信息,请访问:
Spring Boot,Spring Data JPA – 构建 Rest CRUD API 示例
快乐学习!再见。
延伸阅读
- GitHub - FasterXML/jackson-dataformat-xml: Extension for Jackson JSON processor that adds support for serializing POJOs as XML (and deserializing from XML) as an alternative to JSON
- 使用 Spring Security 和 JWT 身份验证保护 Spring Boot 应用程序
- Spring Data JPA 参考文档
部署:
–在 AWS 上部署 Spring Boot 应用程序 – Elastic Beanstalk
– Docker Compose:Spring Boot 和 MySQL 示例
源代码
您可以在Github上找到本教程的完整源代码。