本指南将引导您完成使用Spring创建“hello world”RESTful Web服务的过程,该服务在响应中包含用于跨源资源共享(cors)的头文件。
您将构建一个接受HTTP GET请求的服务:
http://localhost:8080/greeting
并以JSON表示的问候语进行响应:
{"id":1,"content":"Hello, World!"}
您可以自定义参数来请求:
http://localhost:8080/greeting?name=User
name参数值覆盖默认值“world”,并反映在响应中:
{"id":1,"content":"Hello, User!"}
此服务与之前构建RESTfulWeb服务中描述的略有不同,因为它将使用Spring框架的CORS支持来添加相关的CORS响应头。
程序结构
└── src
└── main
└── java
└── hello
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>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service-cors</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot将会你做如下的事:
- 将 classpath 里面所有用到的jar包构建成一个可执行的 JAR 文件,方便执行你的程序
搜索public static void main()方法并且将它当作可执行类 - 根据springboot版本,去查找相应的依赖类版本,当然你可以定义其它版本。
创建资源表示类
既然已经设置了项目和构建系统,就可以创建Web服务了。从考虑服务交互开始这个过程。
服务将处理get请求/greeting,也可以在查询字符串中使用name参数。GET请求应该返回一个200 OK的响应,其中主体中的JSON表示一个greeting。它应该是这样的:
{
"id": 1,
"content": "Hello, World!"
}
id字段是greeting的唯一标识符,content是greeting的文本表示形式。
要为greeting表示建模,请创建一个资源表示类。为id和content数据提供一个具有字段、构造函数和访问器的普通Java对象:
src/main/java/hello/Greeting.java
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting() {
this.id = -1;
this.content = "";
}
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
正如您在下面的步骤中看到的,Spring使用Jackson JSON库自动将Greeting的实例封装成JSON。
接下来,您将创建资源控制器,它将为这些greetings提供服务。
创建控制器
在Spring构建RESTfulWeb服务的方法中,HTTP请求由控制器处理。@Controller注释用来代表控制器,下面的GreetingController通过返回greeting类的新实例来处理获取请求/greeting :
src/main/java/hello/GreetingController.java
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
这个控制器简洁明了,但在里面有很多东西。让我们一步一步地把它分解。
- @RequestMapping注释确保到/greeting的HTTP请求映射到 greeting()方法。
- 上面的示例使用@GetMapping注释作为@RequestMapping(method = RequestMethod.GET)的快捷方式。
- @RequestParam将查询字符串参数name的值绑定到greeting()方法的name参数中。此查询字符串参数不是必须的,如果请求中没有此参数,则使用默认值“world”。
- id用counter.incrementAndGet()来自动生成的。
传统的MVC控制器和上面的RESTful Web服务控制器之间的一个关键区别是HTTP响应主体的创建方式。这个RESTful Web服务控制器不呈现为HTML视图,而是简单地填充并返回一个Greeting对象。对象数据将作为JSON直接写入HTTP响应。
为了实现这一点,greeting()方法上的@ResponseBody注释告诉Spring MVC它不需要通过服务器端视图层呈现greeting对象,而是直接返回的greeting对象。
Greeting 对象必须转换为JSON。由于Spring的HTTP消息转换器支持,您不需要手动执行此转换。因为Jackson在类路径上,所以Spring的MappingJackson2HttpMessageConverter 会自动选择将Greeting实例转换为json。
启用CORS
控制器方法的CORS配置
为了让RESTful Web服务在其响应中包含CORS访问控制头,您只需向处理程序方法添加一个 @CrossOriginn注释:
src/main/java/hello/GreetingController.java
@CrossOrigin(origins = "http://localhost:9000")
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
此@CrossOrigin注释仅为此特定方法启用跨源请求。默认情况下,它允许使用@RequestMapping 注释中指定的所有源、所有headers、HTTP方法以及30分钟的超时。您可以通过指定注释属性之一的值来自定义此行为:origins、methods、allowedHeaders、exposedHeaders、allowCredentials或maxAge。在本例中,我们只允许http://localhost:9000
发送跨源请求。
也可以在控制器类级别添加此注释,以便对此类的所有处理程序方法启用CORS。
全局CORS配置
您还可以定义一些全局CORS配置。这类似于使用基于过滤器的解决方案,但可以在SpringMVC中声明,并与细粒度@CrossOrigin配置结合使用。默认情况下,所有的origins和get、head和post方法都是允许的。
src/main/java/hello/GreetingController.java
@GetMapping("/greeting-javaconfig")
public Greeting greetingWithJavaconfig(@RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
src/main/java/hello/Application.java
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}
您可以轻松地更改任何属性(例如示例中的allowedOrigins),并且只将这个CORS配置应用于特定的路径模式。全局和控制器级CORS配置也可以混用。
创建Application
src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
运行并测试程序
运行Application.java,然后在浏览器中访问: http://localhost:8080/greeting 返回如下:
{"id":1,"content":"Hello, World!"}
在 给一个name参数,访问http://localhost:8080/greeting?name=User,返回
{"id":2,"content":"Hello, User!"}
此更改表明GreetingController中的@RequestParam安排按预期工作。name参数已被赋予默认值“world”,但始终可以通过查询字符串显式重写。
请注意,id自动变化了,这是使用了counter.incrementAndGet()的原因
现在,要测试CORS头文件是否丰存在,并允许来自其他来源的JavaScript客户端访问该服务,您需要创建一个JavaScript客户端来使用该服务。
首先,创建一个名为hello.js的简单javascript文件,其内容如下:
public/hello.js
$(document).ready(function() {
$.ajax({
url: "http://localhost:8080/greeting"
}).then(function(data, status, jqxhr) {
$('.greeting-id').append(data.id);
$('.greeting-content').append(data.content);
console.log(jqxhr);
});
});
此脚本使用jquery来调用http://localhost:8080/greeting
上的rest服务。它由index.html加载,如下所示:
public/index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello CORS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="hello.js"></script>
</head>
<body>
<div>
<p class="greeting-id">The ID is </p>
<p class="greeting-content">The content is </p>
</div>
</body>
</html>
这基本上是在使用jquery调用用RESTful Web服务时创建的REST客户端,稍作修改以使用本地主机端口8080上运行的服务。
因为REST服务已经在本地主机端口8080上运行,所以您需要确保从另一个服务器和/或端口启动客户机。这不仅可以避免两个应用程序之间的冲突,还可以确保客户机代码的来源与服务的来源不同。要启动在本地主机上运行的客户端,端口9000:
mvn spring-boot:run -Dserver.port=9000
一旦客户端启动,在浏览器中打开http://localhost:9000,您将看到:
如果服务响应包含CORS头,那么id和content将呈现到页面中。但是,如果缺少CORS头(或者没有为客户端定义足够的CORS头)