Building a Hypermedia-Driven RESTful Web Service
构建超媒体驱动的RESTful Web服务
This guide walks you through the process of creating a “Hello, World” Hypermedia-driven REST web service with Spring.
本指南将引导您完成使用Spring创建“Hello,World”超媒体驱动的REST Web服务的过程
Hypermedia is an important aspect of REST. It lets you build services that decouple client and server to a large extent and let them evolve independently. The representations returned for REST resources contain not only data but also links to related resources. Thus, the design of the representations is crucial to the design of the overall service.
超媒体是REST的重要方面。它使您可以构建将客户端和服务器在很大程度上分离并使其独立发展的服务。REST资源返回的表示形式不仅包含数据,而且还包含与相关资源的链接。因此,表示的设计对于整体服务的设计至关重要。
What You Will Build
You will build a hypermedia-driven REST service with Spring HATEOAS: a library of APIs that you can use to create links that point to Spring MVC controllers, build up resource representations, and control how they are rendered into supported hypermedia formats (such as HAL).
您将使用Spring HATEOAS构建超媒体驱动的REST服务:一个API库,可用于创建指向Spring MVC控制器的链接,建立资源表示并控制如何将它们呈现为受支持的超媒体格式(例如HAL)
The service will accept HTTP GET requests at http://localhost:8080/greeting
.
该服务将在http://localhost:8080/greeting接受HTTP GET请求
It will respond with a JSON representation of a greeting that is enriched with the simplest possible hypermedia element, a link that points to the resource itself. The following listing shows the output:
它将以问候语的JSON表示形式进行响应,该问候语中包含了最简单的超媒体元素,即指向资源本身的链接
{
"content":"Hello, World!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=World"
}
}
}
The response already indicates that you can customize the greeting with an optional name
parameter in the query string, as the following listing shows:
该响应已经表明您可以使用查询字符串中的可选name参数来自定义问候语,如以下清单所示:
http://localhost:8080/greeting?name=User
The name
parameter value overrides the default value of World
and is reflected in the response, as the following listing shows:
名称参数值将覆盖World的默认值,并反映在响应中,如以下清单所示:
{
"content":"Hello, User!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=User"
}
}
}
What You Need
- About 15 minutes
- A favorite text editor or IDE
- JDK 1.8 or later
- Gradle 6.3+ or Maven 3.6+
- You can also import the code straight into your IDE:
How to complete this guide
Like most Spring Getting Started guides, you can start from scratch and complete each step or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.
像大多数Spring入门指南一样,您可以从头开始完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都可以使用代码。
To start from scratch, move on to Starting with Spring Initializr.
要从头开始,请继续至从Spring Initializr开始
To skip the basics, do the following:
- Download and unzip the source repository for this guide, or clone it using Git:
git clone https://github.com/spring-guides/gs-rest-hateoas.git
- cd into
gs-rest-hateoas/initial
- Jump ahead to Create a Resource Representation Class.
When you finish, you can check your results against the code in gs-rest-hateoas/complete
.
Starting with Spring Initializr
For all Spring applications, you should start with the Spring Initializr. The Initializr offers a fast way to pull in all the dependencies you need for an application and does a lot of the setup for you. This example needs the Spring HATEOAS dependency. The following image shows the Initializr set up for this sample project:
对于所有Spring应用程序,您应该从Spring Initializr开始。Initializr提供了一种快速的方法来提取应用程序所需的所有依赖项,并为您完成许多设置。该示例需要Spring HATEOAS依赖项。下图显示了此示例项目的Initializr设置:
The preceding image shows the Initializr with Maven chosen as the build tool. You can also use Gradle. It also shows values of com.example
and rest-hateoas
as the Group and Artifact, respectively. You will use those values throughout the rest of this sample.
上图显示了选择Maven作为构建工具的Initializr。您也可以使用Gradle。它还将com.programiz和rest-hateoas的值分别显示为Group和Artifact。在本示例的其余部分中,将使用这些值
The following listing shows the pom.xml
file that is created when you choose Maven:
以下清单显示了当您选择Maven时创建的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.programiz</groupId>
<artifactId>rest-hateoas</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest-hateoas</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The following listing shows the build.gradle
file that is created when you choose Gradle:
以下清单显示了在选择Gradle时创建的build.gradle文件:
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.programiz'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
Adding a JSON Library
Because you will use JSON to send and receive information, you need a JSON library. In this guide, you will use the Jayway JsonPath library.
因为您将使用JSON发送和接收信息,所以您需要一个JSON库。在本指南中,您将使用Jayway JsonPath库
To include the library in a Maven build, add the following dependency to your pom.xml
file:
要将库包含在Maven构建中,请将以下依赖项添加到pom.xml文件中:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
To include the libary in a Gradle build, add the following dependency to your build.gradle
file:
要将库包含在Gradle构建中,请将以下依赖项添加到build.gradle文件中:
testCompile 'com.jayway.jsonpath:json-path'
Create a Resource Representation Class
创建资源表示形式类
Now that you have set up the project and build system, you can create your web service.
现在已经设置了项目和构建系统,接下来可以创建Web服务
Begin the process by thinking about service interactions.
通过考虑服务交互来开始该过程
The service will expose a resource at /greeting
to handle GET
requests, optionally with a name
parameter in the query string. The GET
request should return a 200 OK
response with JSON in the body to represent a greeting.
该服务将在/greeting处公开资源以处理GET请求,并可以选择在查询字符串中使用name参数。GET请求应返回200 OK响应,并在正文中使用JSON表示问候
Beyond that, the JSON representation of the resource will be enriched with a list of hypermedia elements in a _links
property. The most rudimentary form of this is a link that points to the resource itself. The representation should resemble the following listing:
除此之外,资源的JSON表示将通过_links属性中的超媒体元素列表来丰富。最基本的形式是指向资源本身的链接。该表示应类似于以下清单:
{
"content":"Hello, World!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=World"
}
}
}
The content
is the textual representation of the greeting. The _links
element contains a list of links (in this case, exactly one with the relation type of rel
and the href
attribute pointing to the resource that was accessed).
内容是问候语的文字表示。_links元素包含一个链接列表(在这种情况下,正是链接的关系类型为rel并且href属性指向所访问的资源的链接)
To model the greeting representation, create a resource representation class. As the _links
property is a fundamental property of the representation model, Spring HATEOAS ships with a base class (called RepresentationModel
) that lets you add instances of Link
and ensures that they are rendered as shown earlier.
要建模问候表示,需创建资源表示类。由于_links属性是表示模型的基本属性,因此Spring HATEOAS附带了一个基类(称为RepresentationModel),该基类使您可以添加Link实例并确保如前所述呈现它们
Create a plain old java object that extends RepresentationModel
and adds the field and accessor for the content as well as a constructor, as the following listing (from src/main/java/com/programiz/resthateoas/Greeting.java
) shows:
创建一个普通的旧Java对象,该对象扩展了RepresentationModel,并添加了内容的字段和访问器以及构造函数,如以下清单(来自src/main/java/com/programiz/resthateoas/Greeting.java)所示:
package com.programiz.resthateoas;
import org.springframework.hateoas.RepresentationModel;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Greeting extends RepresentationModel<Greeting> {
private final String content;
@JsonCreator
public Greeting(@JsonProperty("content") String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
-
@JsonCreator: Signals how Jackson can create an instance of this POJO.
指示Jackson如何创建此POJO的实例
-
@JsonProperty: Marks the field into which Jackson should put this constructor argument.
标记Jackson应将此构造函数参数中放入的字段
As you will see in later in this guide, Spring will use the Jackson JSON library to automatically marshal instances of type Greeting
into JSON.
正如您将在本指南的后面部分看到的那样,Spring将使用Jackson JSON库自动将类型类型Greeting的实例编组为JSON
Next, create the resource controller that will serve these greetings.
接下来,创建将提供这些问候的资源控制器
Create a REST Controller
In Spring’s approach to building RESTful web services, HTTP requests are handled by a controller. The components are identified by the @RestController
annotation, which combines the @Controller
and @ResponseBody
annotations. The following GreetingController
(from src/main/java/com/programiz/resthateoas/GreetingController.java
) handles GET
requests for /greeting
by returning a new instance of the Greeting
class:
在Spring建立RESTful网络服务的方法中,HTTP请求由控制器处理。这些组件由@RestController批注标识,该注解组合了@Controller和@ResponseBody注解。下面的GreetingController(来自src/ main/java/com/programiz/resthateoas/GreetingController.java)通过返回Greeting类的新实例来处理GET请求/greeting:
package com.programiz.resthateoas;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@RestController
public class GreetingController {
private static final String TEMPLATE = "Hello, %s!";
@RequestMapping("/greeting")
public HttpEntity<Greeting> greeting(
@RequestParam(value = "name", defaultValue = "World") String name) {
Greeting greeting = new Greeting(String.format(TEMPLATE, name));
greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());
return new ResponseEntity<>(greeting, HttpStatus.OK);
}
}
This controller is concise and simple, but there is plenty going on. We break it down step by step.
该控制器简洁明了,但是有很多事情要做。我们将其逐步分解
The @RequestMapping
annotation ensures that HTTP requests to /greeting
are mapped to the greeting()
method.
@RequestMapping注解确保将对/greeting的HTTP请求映射到greeting()方法
The above example does not specify GET vs. PUT , POST , and so forth, because @RequestMapping maps all HTTP operations by default. Use @GetMapping("/greeting") to narrow this mapping. In that case you also want to import org.springframework.web.bind.annotation.GetMapping; . |
|
---|---|
上面的示例未指定GET vs. PUT,POST等,因为默认情况下@RequestMapping映射所有HTTP操作。使用@GetMapping(“/greeting”)缩小此映射 |
@RequestParam
binds the value of the query string parameter name
into the name
parameter of the greeting()
method. This query string parameter is implicitly not required
because of the use of the defaultValue
attribute. If it is absent in the request, the defaultValue
of World
is used.
@RequestParam将查询字符串参数名称的值绑定到greeting()方法的名称参数中。由于使用defaultValue属性,因此隐式不需要此查询字符串参数。如果请求中不存在,则使用World的defaultValue。
Because the @RestController
annotation is present on the class, an implicit @ResponseBody
annotation is added to the greeting
method. This causes Spring MVC to render the returned HttpEntity
and its payload (the Greeting
) directly to the response.
由于@RestController注解存在于类中,因此将隐式@ResponseBody注解添加到greeting方法。这使Spring MVC将返回的HttpEntity及其有效负载(问候语)直接呈现给响应
The most interesting part of the method implementation is how you create the link that points to the controller method and how you add it to the representation model. Both linkTo(…)
and methodOn(…)
are static methods on ControllerLinkBuilder
that let you fake a method invocation on the controller. The returned LinkBuilder
will have inspected the controller method’s mapping annotation to build up exactly the URI to which the method is mapped.
方法实现中最有趣的部分是如何创建指向控制器方法的链接以及如何将其添加到表示模型。linkTo(…)和methodOn(…)都是ControllerLinkBuilder上的静态方法,使您可以伪造控制器上的方法调用。返回的LinkBuilder将检查控制器方法的映射注解,以准确建立该方法映射到的URI。
Spring HATEOAS respects various X-FORWARDED- headers. If you put a Spring HATEOAS service behind a proxy and properly configure it with X-FORWARDED-HOST headers, the resulting links will be properly formatted. |
|
---|---|
Spring HATEOAS尊从各种X-FORWARDED-标头。如果将Spring HATEOAS服务放在代理后面,并使用X-FORWARDED-HOST标头正确配置它,则将正确格式化结果链接 |
The call to withSelfRel()
creates a Link
instance that you add to the Greeting
representation model.
对withSelfRel()的调用将创建一个Link实例,该实例将添加到Greeting表示模型中
@SpringBootApplication
is a convenience annotation that adds all of the following:
@Configuration
: Tags the class as a source of bean definitions for the application context.@EnableAutoConfiguration
: Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings. For example, ifspring-webmvc
is on the classpath, this annotation flags the application as a web application and activates key behaviors, such as setting up aDispatcherServlet
.@ComponentScan
: Tells Spring to look for other components, configurations, and services in thecom/example
package, letting it find the controllers.
package com.example.resthateoas;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RestHateoasApplication {
public static void main(String[] args) {
SpringApplication.run(RestHateoasApplication.class, args);
}
}
The main()
method uses Spring Boot’s SpringApplication.run()
method to launch an application. Did you notice that there was not a single line of XML? There is no web.xml
file, either. This web application is 100% pure Java and you did not have to deal with configuring any plumbing or infrastructure.
Build an executable JAR
You can run the application from the command line with Gradle or Maven. You can also build a single executable JAR file that contains all the necessary dependencies, classes, and resources and run that. Building an executable jar makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.
If you use Gradle, you can run the application by using ./gradlew bootRun
. Alternatively, you can build the JAR file by using ./gradlew build
and then run the JAR file, as follows:
java -jar build/libs/rest-hateoas-1.0.jar
If you use Maven, you can run the application by using ./mvnw spring-boot:run
. Alternatively, you can build the JAR file with ./mvnw clean package
and then run the JAR file, as follows:
java -jar target/rest-hateoas-1.0.jar
The steps described here create a runnable JAR. You can also build a classic WAR file. | |
---|---|
此处描述的步骤将创建可运行的JAR。您也可以构建经典的WAR文件 |
Logging output is displayed. The service should be up and running within a few seconds.
Test the Service
Now that the service is up, visit http://localhost:8080/greeting, where you should see the following content:
{
"content":"Hello, World!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=World"
}
}
}
Provide a name
query string parameter by visiting the following URL: http://localhost:8080/greeting?name=User
. Notice how the value of the content
attribute changes from Hello, World!
to Hello, User!
and the href
attribute of the self
link reflects that change as well, as the following listing shows:
通过访问以下URL提供名称查询字符串参数:http://localhost:8080/greeting?name=User。注意content属性的值如何从Hello,World!中更改为Hello, User!self链接的href属性也反映了这一变化,如以下清单所示:
{
"content":"Hello, User!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=User"
}
}
}
This change demonstrates that the @RequestParam
arrangement in GreetingController
works as expected. The name
parameter has been given a default value of World
but can always be explicitly overridden through the query string.
此更改说明GreetingController中的@RequestParam安排按预期工作。名称参数的默认值为World,但始终可以通过查询字符串显式覆盖
Summary
Congratulations! You have just developed a hypermedia-driven RESTful web service with Spring HATEOAS.