Http入站适配器在CORS

时间:2018-05-08 07:23:33

标签: cors spring-integration spring-integration-http

在我的开发环境中,我正在为前端和后端使用两个独立的技术堆栈:

  • 前端的React + webpack-dev-server,在localhost:8081
  • 提供
  • 后端的Spring,以localhost:7080为基础提供HTTP端点,有两种方式:
    • 使用集成进行数据处理(接收,转换和存储),此处理从http:inbound-channel-adapter开始(请参阅以下XML配置)
    • 使用 MVC 通过REST API提供只读(即仅限GET)访问(使用@RestController - 带注释的类实现)

前端仅使用fetch()次调用与后端进行互动。

在不同的端口上,浏览器在没有正确的CORS配置的情况下阻止对HTTP端点的任何请求。由于这将是暂时的,我试图提出最简单/最快的解决方案,我设法使REST API调用工作,但入站适配器仍然在OPTIONS请求期间出现问题。

webapp有两个部分:

  1. 从REST API中提取数据的表
    • 此部分有效,前端成功从后端检索数据
  2. 上传包含其他几个字段的文件的表单
    • 这里我一直在犯错误
  3. 这是当前的Spring Integration配置(我使用的是5.0.3版):

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:ctx="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:int="http://www.springframework.org/schema/integration"
        xmlns:int-file="http://www.springframework.org/schema/integration/file"
        xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
        xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
        xmlns:int-http="http://www.springframework.org/schema/integration/http"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="...">
    
        <!-- ... -->
    
        <int-http:inbound-channel-adapter id="httpInboundAdapter"
            channel="httpRequestsChannel"
            path="/services/requests"
            supported-methods="POST">
            <int-http:cross-origin allow-credentials="false" allowed-headers="*" origin="*" />
        </int-http:inbound-channel-adapter>
    
        <int:channel id="httpRequestsChannel" />
    
        <int:chain input-channel="httpRequestsChannel" id="httpRequestsChain">
    
            <int:transformer method="transform">
                <bean class="transformers.RequestToMessageFile" />
            </int:transformer>
    
        <!-- ... -->
    
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
    
        <!-- ... -->
    
    </beans>
    

    这就是其中一个REST端点的完成方式:

    package api;
    
    // imports
    
    @CrossOrigin(origins = "*", maxAge = 3600)
    @RestController
    @RequestMapping(path="/api/requests", produces="application/json")
    public class RequestController {
        // ...
    

    这是应该处理POSTed文件的转换器:

    package transformers;
    
    // imports
    
    public class RequestToMessageFile {
    
        private static final String ALLOW_DUPES = "allowDuplicateFilenames";
    
        public Message<?> transform(LinkedMultiValueMap<String, Object> multipartRequest) {
            File file = null;
            int tx = -1;
    
            String localFilename = null;
    
            for (String elementName : multipartRequest.keySet()) {
                if (RequestFields.TRANSACTION_TYPE.equals(elementName)) {
                    tx = Integer.valueOf(((String[]) multipartRequest.getFirst(RequestFields.TRANSACTION_TYPE))[0]);
                } else if (RequestFields.FILE.equals(elementName)){
                    try {
                        UploadedMultipartFile uploadedFile = (UploadedMultipartFile) multipartRequest.getFirst(RequestFields.FILE);
    
                        // file handling
                        file = new File(/* ... */);
                        uploadedFile.transferTo(file);
                    } catch (IllegalStateException e) {
                        LogManager.getLogger(getClass()).error(e, e);
                    } catch (IOException e) {
                        LogManager.getLogger(getClass()).error(e, e);
                    }
                }
            }
    
            if(file != null) {
                MessageBuilder<File> builder = MessageBuilder.withPayload(file);
                // ...
                Message<?> m = builder.build();
                return m;
            } else {
                return MessageBuilder.withPayload(new Object()).build();
            }
        }
    
    }
    

    这就是我从浏览器调用端点的方式:

    var data = new FormData();
    data.append('transaction-type', this.state.transactionType);
    data.append('file', this.state.file);
    
    fetch('http://localhost:7080/services/requests?allowDuplicateFilenames=true', {
        method: 'POST',
        body: data,
        mode: 'cors',
        headers:{
            'Accept': 'application/json',
            },
      }
    )
    .then(/* ... */)
    .catch(/* ... */)
    

    我所遇到的是在OPTIONS请求期间调用变换器(我没想到),当然不携带任何主体,因此只有allowDuplicateFilenames url参数存在。< / p>

    由于无法确定文件,变换器正在返回消息withPayload(new Object()),但请求在配置的通道和端点上继续前进,最终在空数据传递到最后时出现错误端点 - jdbc:outbound-channel-adapter

    错误最终以HTTP 500错误的形式返回给客户端。

    我希望只将POST请求传递给http入站适配器,而OPTIONS请求由框架处理,但这可能是错误的解释。

    什么阻止POST请求成功?我的配置有问题吗?我是否应该处理OPTIONS请求?

    在后续尝试中,我已将Allow-Control-Allow-Origin: *扩展程序添加到Chrome,并从前端headers调用中删除了fetch()参数。使用此设置,请求可以正常工作,因为浏览器只发出POST

    我想添加对REST API的请求是成功的,因此CORS配置必须正确;虽然我认为http入站适配器具有与@RestController相同的配置,但必须缺少某些内容。

    更新

    我添加了另一个端点来专门处理OPTIONS请求:

    </int-http:inbound-channel-adapter>
    
        <int-http:inbound-channel-adapter id="httpInboundAdapterOptions"
            channel="nullChannel"
            path="/services/requests"
            supported-methods="OPTIONS">
            <int-http:cross-origin allow-credentials="false" allowed-headers="*" origin="*" />
        </int-http:inbound-channel-adapter>
    

    现在收到OPTIONSPOST请求,后者启动整个集成数据处理;浏览器仍然抱怨丢失“Access-Control-Allow-Origin”标题,从浏览器控制台我只能看到标题:

    HTTP/1.1 200 OK
    Server: Apache-Coyote/1.1
    Content-Length: 0
    Date: Tue, 08 May 2018 14:29:30 GMT
    

    相比之下,REST端点上的GET带来了这些:

    HTTP/1.1 200 OK
    Server: Apache-Coyote/1.1
    Vary: Origin
    Access-Control-Allow-Origin: *
    Content-Type: application/json;charset=UTF-8
    Transfer-Encoding: chunked
    Date: Tue, 08 May 2018 14:29:08 GMT
    

0 个答案:

没有答案