Spring Boot REST XML - 在 RESTFul 应用程序中提供 XML 数据

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(438)   2023-06-26 07:54:18

Spring Boot REST XML 教程展示了如何在 Spring Boot RESTFul 应用程序中提供 XML 数据。我们为 RESTful 控制器创建测试方法。

春天 是用于创建企业应用程序的流行 Java 应用程序框架。 弹弓贴 是 Spring 框架进化的下一步。它有助于以最小的努力创建独立的、生产级的基于 Spring 的应用程序。它促进在 XML 配置上使用约定优于配置 原则。

RESTFul 应用程序

RESTFul 应用程序遵循 REST 架构风格,用于设计网络应用程序。 RESTful 应用程序生成对资源执行 CRUD(创建/读取/更新/删除)操作的 HTTP 请求。 RESTFul 应用程序通常以 JSON 或 XML 格式返回数据。

可扩展标记语言 (XML) 是一种标记语言,它定义了一组规则,用于以人类可读和机器可读的格式对文档进行编码。 XML 通常用于应用程序之间的数据交换。

Spring Boot REST XML 示例

以下应用程序是一个 Spring Boot RESTful 应用程序,它使用 Spring Data JPA 从 H2 数据库返回 XML 格式的数据。

pom.xml
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── Application.java
│   │           ├── model
│   │           │   ├── Cities.java
│   │           │   └── City.java
│   │           ├── controller
│   │           │   └── MyController.java
│   │           ├── repository
│   │           │   └── CityRepository.java
│   │           └── service
│   │               ├── CityService.java
│   │               └── ICityService.java
│   └── resources
│       ├── application.yml
│       └── import.sql
└── test
    └── java
        └── com
            └── zetcode
                └── test
                    └── RestControllerTest.java

这是项目结构。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
            http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zetcode</groupId>
    <artifactId>springbootrestxml</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
    </parent>

    <dependencies>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.7</version>
            </plugin>
        </plugins>
    </build>

</project>

这是 Maven 构建文件。 h2 依赖项包括 H2 数据库驱动程序。 jackson-dataformat-xml 添加了 Jackson XML 序列化器和反序列化器。

Spring Boot 启动器是一组方便的依赖描述符,极大地简化了 Maven 配置。 spring-boot-starter-parent 有一些 Spring Boot 应用程序的通用配置。 spring-boot-starter-web 是使用 Spring MVC 构建 Web 应用程序的入门工具,包括 RESTFul 应用程序。它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-starter-data-jpa 是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。 spring-boot-starter-test 是一个用于使用包括 JUnit、Hamcrest 和 Mockito 在内的库测试 Spring Boot 应用程序的入门工具。

由于 JAXB API 在 Java 11 中已从 Java SE 中移除,因此我们需要添加 jaxb-api 依赖项。

spring-boot-maven-plugin 在 Maven 中提供 Spring Boot 支持,允许我们打包可执行的 JAR 或 WAR 存档。它的 spring-boot:run 目标运行 Spring Boot 应用程序。

resources/application.yml
server:
  port: 8086
  servlet:
    context-path: /rest

spring:
  main:
    banner-mode: "off"
jpa:
    database: h2
    hibernate:
    dialect: org.hibernate.dialect.H2Dialect
    ddl-auto: create-drop

logging:
  level:
    org:
    springframework: ERROR

application.yml 文件中,我们编写了 Spring Boot 应用程序的各种配置设置。 port 设置服务器端口和 context-path 上下文路径(应用程序名称)。完成这些设置后,我们在 localhost:8086/rest/ 访问应用程序。通过 banner-mode 属性,我们关闭了 Spring 横幅。

JPA database 值指定要操作的目标数据库。在我们的例子中,我们指定了 Hibernate 方言 org.hibernate.dialect.H2Dialectddl-auto是数据定义语言模式; create-drop 选项自动创建和删除数据库模式。

H2数据库在内存中运行。此外,我们将 spring 框架的日志记录级别设置为 ERROR。 application.yml 文件位于 src/main/resources 目录中。

com/zetcode/model/City.java
package com.zetcode.model;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Objects;

@Entity
@Table(name = "cities")
@JacksonXmlRootElement(localName = "City")
public class City implements Serializable {

    private static final long serialVersionUID = 21L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JacksonXmlProperty(isAttribute = true)
    private Long id;

    @JacksonXmlProperty
    private String name;

    @JacksonXmlProperty
    private int population;

    public City() {
    }

    public City(Long id, String name, int population) {
        this.id = id;
        this.name = name;
        this.population = population;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public String toString() {
        return "City{" + "id=" + id + ", name=" + name
                + ", population=" + population + '}';
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 37 * hash + Objects.hashCode(this.id);
        hash = 37 * hash + Objects.hashCode(this.name);
        hash = 37 * hash + this.population;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final City other = (City) obj;
        if (this.population != other.population) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }

        return Objects.equals(this.id, other.id);
    }
}

这是 City 实体。每个实体必须至少定义两个注释:@Entity@Id。之前,我们将 ddl-auto 选项设置为 create-drop,这意味着 Hibernate 将从该实体创建表模式。

@Entity
@Table(name = "cities")
@JacksonXmlRootElement(localName = "City")
public class City implements Serializable {

@Entity 注释指定该类是一个实体并映射到数据库表。 @Table 注释指定要用于映射的数据库表的名称。使用 @JacksonXmlRootElement(localName = "City") 注释,我们设置了 XML 输出根元素的名称。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JacksonXmlProperty(isAttribute = true)
private Long id;

@Id 注解指定实体的主键,@GeneratedValue 提供生成主键值的策略。使用 @JacksonXmlProperty(isAttribute = true) 我们将 id 设置为 City< 的属性/code> XML 输出中的元素。

@JacksonXmlProperty
private String name;

@JacksonXmlProperty
private int population;

使用 @JacksonXmlProperty,我们将 namepopulation 属性设置为 City 的属性XML 输出中的元素。

com/zetcode/model/Cities.java
package com.zetcode.model;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@JacksonXmlRootElement
public class Cities implements Serializable {

    private static final long serialVersionUID = 22L;

    @JacksonXmlProperty(localName = "City")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<City> cities = new ArrayList<>();

    public List<City> getCities() {
        return cities;
    }

    public void setCities(List<City> cities) {
        this.cities = cities;
    }
}

Cities bean 是一个辅助 bean,用于获得更好的 XML 输出。

@JacksonXmlProperty(localName = "City")
@JacksonXmlElementWrapper(useWrapping = false)
private List<City> cities = new ArrayList<>();

使用 @JacksonXmlProperty@JacksonXmlElementWrapper 注释,我们确保我们有 City 元素嵌套在Cities 元素用于城市对象的 ArrayList

resources/import.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);

该模式由 Hibernate 自动创建;稍后,执行 import.sql 文件以用数据填充 H2 表。

com/zetcode/repository/CityRepository.java
package com.zetcode.repository;

import com.zetcode.bean.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CityRepository extends CrudRepository<City, Long> {

}

通过从 Spring CrudRepository 扩展,我们将为我们的数据存储库实现一些方法,包括 findAll()findById()。这样我们就节省了很多样板代码。

com/zetcode/service/ICityService.java
package com.zetcode.service;

import com.zetcode.model.Cities;
import com.zetcode.model.City;

public interface ICityService {

    Cities findAll();
    City findById(Long id);
}

ICityService 提供了获取所有城市和通过Id获取一个城市的契约方法。

com/zetcode/service/CityService.java
package com.zetcode.service;

import com.zetcode.model.Cities;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CityService implements ICityService {

    @Autowired
    private CityRepository repository;

    @Override
    public Cities findAll() {

        var cities = (List<City>) repository.findAll();
        var mycities = new Cities();
        mycities.setCities(cities);

        return mycities;
    }

    @Override
    public City findById(Long id) {

        return repository.findById(id).orElse(new City());
    }
}

CityService 包含 findAll()findById() 方法的实现。我们使用存储库来处理数据。

@Autowired
private CityRepository repository;

CityRepository 被注入。

@Override
public Cities findAll() {

    var cities = (List<City>) repository.findAll();
    var mycities = new Cities();
    mycities.setCities(cities);

    return mycities;
}

请注意,findAll() 方法返回 Cities bean。

@Override
public City findById(Long id) {

    return repository.findById(id).orElse(new City());
}

findById() 服务方法调用存储库的 findById() 方法通过其 Id 获取城市;如果找不到城市,则返回一个空城市。

com/zetcode/controller/MyController.java
package com.zetcode.controller;

import com.zetcode.model.Cities;
import com.zetcode.model.City;
import com.zetcode.service.ICityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @Autowired
    private ICityService cityService;

    @GetMapping(value="/cities", produces=MediaType.APPLICATION_XML_VALUE)
    public Cities findCities() {

        return cityService.findAll();
    }

    @GetMapping(value="/cities/{cityId}", produces=MediaType.APPLICATION_XML_VALUE)
    public City findCity(@PathVariable Long cityId) {

        return cityService.findById(cityId);
    }
}

这是 Spring Boot RESTful 应用程序的控制器类。 @RestController 注释创建一个 RESTful 控制器。传统的 MVC 控制器使用 ModelAndView,而 RESTful 控制器只是简单地返回对象,对象数据以 JSON 或 XML 格式直接写入 HTTP 响应(通常)。

@Autowired
private ICityService cityService;

我们将 ICityService 注入到 countryService 字段中。

@GetMapping(value="/cities", produces=MediaType.APPLICATION_XML_VALUE)
public Cities findCities() {

    return cityService.findAll();
}

我们将带有 /cities 路径的请求映射到控制器的 findCities() 方法。默认请求是 GET 请求。通过使用 MediaType.APPLICATION_XML_VALUE,Spring 使用了一个生成 XML 数据的消息转换器。

@GetMapping(value="/cities/{cityId}", produces=MediaType.APPLICATION_XML_VALUE)
public City findCity(@PathVariable Long cityId) {

    return cityService.findById(cityId);
}

在第二种方法中,我们返回一个特定的城市。 URL路径包含要检索的城市的Id;我们使用 @PathVariable 注释将 URL 模板变量绑定到 cityId 参数。

com/zetcode/test/RestControllerTest.java
package com.zetcode.test;

import java.util.List;

import com.zetcode.bean.City;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RestControllerTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Value("http://localhost:${local.server.port}/${server.servlet.context-path}/cities")
    private String appPath;

    private City c1, c2, c3;

    @Before
    public void setUp() {

        this.c1 = new City(1L, "Bratislava", 432000);
        this.c2 = new City(2L, "Budapest", 1759000);
        this.c3 = new City(3L, "Prague", 1280000);
    }

    @Test
    public void allCitiesTest() {

        var paramType = new ParameterizedTypeReference<List<City>>() { };
        var cities = restTemplate.exchange(appPath, HttpMethod.GET, null, paramType);

        assertThat(cities.getBody()).hasSize(8);
        assertThat(cities.getBody()).contains(this.c1, this.c2, this.c3);
    }

    @Test
    public void oneCity() {

        var city = this.restTemplate.getForObject(appPath + "/1/", City.class);
        assertThat(city).extracting("name", "population").containsExactly("Bratislava",
                432000);
    }
}

RestControllerTest 包含两个测试控制器方法的方法。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

Application 设置 Spring Boot 应用程序。 @SpringBootApplication 启用自动配置和组件扫描。

$ mvn spring-boot:run

使用 mvn spring-boot:run 命令,我们运行应用程序。该应用程序部署在嵌入式 Tomcat 服务器上。

$ curl localhost:8086/rest/cities
<Cities>
<City id="1"><name>Bratislava</name><population>432000</population></City>
<City id="2"><name>Budapest</name><population>1759000</population></City>
<City id="3"><name>Prague</name><population>1280000</population></City>
<City id="4"><name>Warsaw</name><population>1748000</population></City>
<City id="5"><name>Los Angeles</name><population>3971000</population></City>
<City id="6"><name>New York</name><population>8550000</population></City>
<City id="7"><name>Edinburgh</name><population>464000</population></City>
<City id="8"><name>Berlin</name><population>3671000</population></City>
</Cities>

使用 curl 命令,我们可以获取所有城市。

$ curl localhost:8086/rest/cities/1
<City id="1"><name>Bratislava</name><population>432000</population></City>

在这里,我们得到一个由其 Id 标识的城市。

在本教程中,我们从 Spring Boot RESTful 应用程序以 XML 格式将数据返回给客户端。我们使用 Spring Data JPA 从 H2 数据库中检索数据。

地址:https://www.cundage.com/article/springboot-restxml.html

相关阅读

Spring Boot Vue.js 教程展示了如何使用 Vue.js 框架创建一个简单的 Spring Boot。 Vue.js Vue.js 是一个用于构建用户界面的 JavaScript ...
JavaScript 是否已经取代 Java 成为新的“一次编写,随处运行”的编程语言?这完全取决于您的观点。随着 WebAssembly 等技术的出现,Java 可以在新奇的“一次编写,随处编...
Usage of TypeScript,微软基于 JavaScript 的强类型语言, has soared compared to six years ago, according to th...
云莓将基于 Spring 构建的 Java 后端与使用 Lit 构建的 TypeScript 前端相结合,一个快速、响应式的 JavaScript 框架。基于 Vaadin Fusion 的 H...
本博客严重偏向于 GWT(和基于 GWT 的框架),但我们牢记 GWT 将来可能会被其他技术接管,因此我们始终对探索其他平台/框架持开放态度。正如他们所说,多元化可以降低风险。每种编程语言,即使...
Java JSON 教程展示了如何使用 JSON-Java 在 Java 中进行 JSON 序列化和反序列化。 JSON(JavaScript 对象显示法) 是一种轻量级数据交换格式。人类易于读...
JHipster 是一个长期存在且雄心勃勃的混合 Java 和 JavaScript 项目,致力于使用现代反应式前端简化全栈 Java 应用程序的开发。 JHipster 开发团队不断发布新版本...
解析器是强大的工具,使用 ANTLR 可以编写可用于多种不同语言的各种解析器。 在这个完整的教程中,我们将: 解释基础:什么是解析器,它可以用来做什么 查看如何设置 ANTLR 以便在 Java...
Spring Boot JSON 教程展示了如何在 Spring Boot 注释中提供 JSON 数据。 春天 是一个流行的 Java 应用程序框架,弹簧贴 是 Spring 的演变,有助于创建...
根据最近一项全球开发人员调查,在开发人员偏好方面,JavaScript 和 Python 保持了持久力,而 锈 的使用率正在上升. 5 月 4 日的一份题为“开发者国家情况,第 22nd 版”的...