Spring Cloud Function AWS Lambda 与 APIGW 上的代理集成:CORS 问题

Spring Cloud Function AWS Lambda with proxy integration on APIGW : CORS issue

我们已经使用 Spring 云函数创建了 AWS Lambda 函数。此函数 returns APIGatewayProxyResponseEvent 响应。下面的示例

{
    "statusCode": 200,
    "headers": {
        "Access-Control-Expose-Headers": "Access-Control-Allow-Methods,Access-Control-Allow-Origin",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
        "Access-Control-Max-Age": "200",
        "Access-Control-Allow-Headers": "Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers",
        "Content-Type": "application/json"
    },
    "multiValueHeaders": null,
    "body": "response Data json Body",
    "isBase64Encoded": false
}

APIGW 使用 Lambda 代理集成,因此没有响应映射选项。 我们通过在控制台上使用 Actions 启用了 CORS。这会自动添加 OPTIONS 方法,我们在下面配置了 200 响应 headers

Access-Control-Max-Age          :   '200'   
Access-Control-Allow-Headers    :   'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'  
Access-Control-Expose-Headers   :   'Access-Control-Allow-Methods,Access-Control-Allow-Origin'  
Access-Control-Allow-Origin     :   '*' 
Access-Control-Allow-Methods    :   'GET,OPTIONS'

以上步骤与AWS文档同步AWS - How to CORS Lambda proxy

我们在一个阶段部署了 API 并且能够通过 Postman 访问它。从当前位于本地主机上的 web-application 访问时,我们收到 CORS 错误。

在“网络”选项卡中,可以看到预检请求(选项)returns 200 OK 和所需的 CORS headers。然而,实际的 GET 调用仍然失败,显示“CORS 错误”。

问题是 APIGW 没有将 APIGatewayProxyResponseEvent object 中返回的 headers 复制到最终的 APIGW 响应 headers

这是已知问题还是我遗漏了什么

编辑

APIGW lambda 代理的屏幕截图

APIGW 响应的屏幕截图(从控制台测试)

浏览器开发人员选项中的网络选项卡显示预检请求成功

编辑2

添加控制台输出

编辑 1

检查 spring 云里程碑版本时,此问题已从 3.2.0-M1 开始得到解决。 (当前可用版本是 3.1.5)。一旦发布,我以前发送 APIGatewayProxyResponseEvent 作为输出的方法就可以正常工作。

@Oleg Zhurakousky可以确认

下面的原始答案:(几乎是解决方法)

从 AWS 支持处获得帮助,了解到 return 来自 Spring 云函数的响应正在被修改。这反过来导致所有 headers 被封装为 Lambda 响应的一部分 body.

我之前的函数实现是

 @Bean
    public Function<APIGatewayProxyRequestEvent,APIGatewayProxyResponseEvent> testFunc2(){
        return event -> {
            System.out.println(event);
            APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
            Map<String,String> hdr = new HashMap<>();
            hdr.put("Access-Control-Allow-Origin","*");
            response.setHeaders(hdr);
            response.setStatusCode(HttpStatus.OK.value());
            response.setBody("Hello World!");
            return response;
        };
    }

我必须将其更改为以下内容以确保 headers 被视为 http headers 而不是 lambda 响应的一部分 body

@Bean
    public Function<APIGatewayProxyRequestEvent, Message<APIGatewayProxyResponseEvent>> testFunc3(){
        return event -> {
            System.out.println(event);
            APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
            Map<String,Object> hdr = new HashMap<>();
            hdr.put("Access-Control-Allow-Origin","*");
            response.setStatusCode(HttpStatus.OK.value());
            response.setBody("Hello World!");
            Message<APIGatewayProxyResponseEvent> finalResponse = new GenericMessage<APIGatewayProxyResponseEvent>(response,hdr);
            System.out.println("Response prepared " +response);
            System.out.println("Final Response being returned " +finalResponse);
            return finalResponse;
        };
    }

Spring 云函数的实际入口点是 org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest

此处在准备响应时 spring 从函数中获取 returned 值作为消息 (org.springframework.messaging) 负载。

因此,为了设置 http headers,我们需要 return Message 而不是 APIGatewayProxyResponseEvent。在这里,我们在 Message headers.

中明确设置我们的 http headers