本指南将引导您完成使用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头)