Kotlin 和 Spring Boot 中的 CORS 预检错误

Posted

技术标签:

【中文标题】Kotlin 和 Spring Boot 中的 CORS 预检错误【英文标题】:CORS Preflight Error In Kotlin and Springboot 【发布时间】:2021-09-09 19:43:22 【问题描述】:

这是针对以下错误的绝对最小测试用例:

Access to fetch at 'http://localhost:8080/comment' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

首先,我从https://start.spring.io/下载以下项目

仅有的两个依赖项是 Spring Web 和 Rest Repositories

然后我将以下内容添加到我的 DemoApplication.kt 文件中:

package com.example.demo //original line

import org.springframework.boot.autoconfigure.SpringBootApplication //original line
import org.springframework.boot.runApplication //original line

import org.springframework.web.bind.annotation.GetMapping //added line
import org.springframework.web.bind.annotation.PostMapping //added line
import org.springframework.web.bind.annotation.RestController //added line
import org.springframework.web.bind.annotation.CrossOrigin //added line
import org.springframework.web.bind.annotation.RequestMapping //added line
import org.springframework.web.bind.annotation.RequestBody //added line

@SpringBootApplication //original line
class DemoApplication //original line

fun main(args: Array<String>)  //original line
    runApplication<DemoApplication>(*args) //original line
 //original line

//below all added lines

@RestController
@CrossOrigin(origins = ["http://localhost:3000"], maxAge=3600, allowCredentials = "true")
@RequestMapping("/test")
public class RequestTest
    
    @GetMapping
    // @CrossOrigin(origins = ["http://localhost:3000"], maxAge=3600, allowCredentials = "true")
    fun stringfunc():String
        println("inside the index of requesttest")
        return "some string"
    

    @RequestMapping("/comment")
    // @CrossOrigin(origins = ["http://localhost:3000"], maxAge=3600, allowCredentials = "true")
    fun getComment() : String 
        val comment: String = "i hope this works"
        return comment
    


这里是 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.5.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <kotlin.version>1.5.20</kotlin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-kotlin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
        </dependency>

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

    <build>
        <sourceDirectory>$project.basedir/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>$project.basedir/src/test/kotlin</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <configuration>
                    <args>
                        <arg>-Xjsr305=strict</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>$kotlin.version</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

构建输出显示没有错误并且服务器正在运行。

我的 javascript 获取请求是 -

const fetchrequest = () =>  
  fetch('http://localhost:8080/comment')
  .then(response => response.json())
  .then(data => console.log(data))

这应该是@CrossOrigin 应该使用的绝对最小的应用程序。不知道是什么问题。

【问题讨论】:

您似乎在混合 Spring 的 Web 框架,因为您同时依赖于 starter-web 和 starter-webflux。当两者都存在时,首选基于 Servlet 的 starter-web。这意味着您的基于 WebFlux 的 WebFilter 无效。我建议将您的应用程序剥离到重现问题所需的最低限度。 我已经删除了 starter-webflux 并且还删除了 spring-security。仍然有相同的预检错误。我在上面复制了一个最小的应用程序。 CORS 仅涉及 Web 层,因此删除 mysql、PostgreSQL 和 JPA 依赖项,您可能还可以删除验证和 devtools 依赖项。 ...并且您有一个类级别的 CORS 注释以及方法级别的 CORS 注释,因此您可以尝试其中一个而不是两者,并且因为您的测试只是关于 GET @ 987654331@去掉其他方法,用@GetMapping('/comment')代替@RequestMapping 【参考方案1】:

试试这个:

@SpringBootApplication
class KotlinServerApplication

fun main(args: Array<String>) 
    runApplication<KotlinServerApplication>(*args)


@RestController
@CrossOrigin(origins = ["http://localhost:3000"], maxAge=3600, allowCredentials = "true")
public class RequestTest
    
    @GetMapping("/comment")
    fun getComment() : Comment 
        val comment = Comment(
            author = "test",
            content = "test",
        )
        return comment
    


【讨论】:

我在上面复制了一个最小的测试用例示例。仍然出现同样的错误。 发现问题 - fetch('localhost:8080/test/comment')。鉴于这是正确的答案,我会给予你信任。【参考方案2】:

第一次处理 CORS 时可能会让人头疼。这里有一些 useful information 帮助我理解了它的含义。

如果您想要全局 CORS 配置,我建议您使用 @Configuration 而不是类注释。添加一个新类:

@Configuration
@ComponentScan
public class RestConfiguration 

    @Bean
    public WebMvcConfigurer corsConfigurer() 
        return new WebMvcConfigurer() 
            @Override
            public void addCorsMappings(CorsRegistry registry) 
                registry.addMapping("/**").allowedOrigins("https://my.allowed.origin.com");
            
        ;
    


它是 Java,但我认为您可以处理它。 此外,this 较早的答案可能会有所帮助。

【讨论】:

以上是关于Kotlin 和 Spring Boot 中的 CORS 预检错误的主要内容,如果未能解决你的问题,请参考以下文章

干货丨Kotlin在Spring Boot中的应用

Kotlin Spring Boot 中的控制器验证

为啥@JacksonXmlProperty 使用 Kotlin 忽略 Spring Boot 中的参数?

kotlin + Spring boot 中的单元测试休息控制器

在 pom.xml 中为 Kotlin 中的 Spring Boot 项目指定 Main 类

具有不可变属性的@ConstructorBinding 不适用于 Spring Boot Kotlin 中的 @Value @ConfigurationProperties