Azure APIM 在预检和从 axios 发出的 GET 请求时返回空响应正文,状态代码为 200

Posted

技术标签:

【中文标题】Azure APIM 在预检和从 axios 发出的 GET 请求时返回空响应正文,状态代码为 200【英文标题】:Azure APIM returns empty response body with status code 200 on preflight and GET requests made from axios 【发布时间】:2021-09-21 20:00:11 【问题描述】:

我们在 Azure API 管理中添加了一个 REST API。 但是,当尝试从我们的前端(使用 axios 的 Vue.js)访问它时,请求失败并且我们收到一条错误消息:“从源 https://test-vue-backend.io/ 访问 XMLHttpRequest ://test-vue-frontend.io' 已被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头。” GET 请求和 POST 请求。

对于 POST 请求,我可以看到正在发出 Preflight 请求。 此请求获得 200 状态代码作为响应,但响应正文为空。

预检请求的错误消息是“从源 'https://test-vue-frontend.io' 访问 'https://test-vue-backend.io/' 的 XMLHttpRequest 已被 CORS 阻止策略:对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。”

我没有看到 GET 请求的预检请求。它只是立即失败并显示第一个提到的错误消息。

这是该 API 顶层的策略(所有操作):

<policies>
    <inbound>
        <base />
        <cors>
            <allowed-origins>
                <origin>https://test-vue-frontend.io</origin>
            </allowed-origins>
            <allowed-methods preflight-result-max-age="300">
                <method>GET</method>
                <method>POST</method>
                <method>PATCH</method>
                <method>DELETE</method>
                <method>OPTIONS</method>
            </allowed-methods>
            <allowed-headers>
                <header>*</header>
            </allowed-headers>
        </cors>
        <choose>
            <when condition="@(context.Request.OriginalUrl.Host.ToLower() == "test-vue-backend.io")">
                <set-backend-service base-url="http://localhost:8080/backend/webapi" />
            </when>
            <when condition="@(context.Request.OriginalUrl.Host.ToLower() == "dev-vue-backend.io")">
                <set-backend-service base-url="http://localhost:8081/backend/webapi" />
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
        <set-header name="Access-Control-Allow-Origin" exists-action="override">
            <value>@(context.Request.Headers.GetValueOrDefault("Origin",""))</value>
        </set-header>
        <set-header name="Access-Control-Allow-Credentials" exists-action="override">
            <value>true</value>
        </set-header>
        <set-header name="Access-Control-Allow-Methods" exists-action="override">
            <value>GET, POST, PATCH, DELETE, OPTIONS</value>
        </set-header>
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

出于隐私原因,所有 API 名称都已更改,但我可以向您保证,它们在我的配置中是正确的,不是问题的原因。

axios POST 和 GET 请求如下所示:

axios
  .get("https://test-vue-backend.io/getroute?lang=" + lang)
  .then(response => 
    commit("SET_ADVICEDATA", response.data);
  );

axios
  .post("https://test-vue-backend.io/postuser", null, 
    headers:  Authorization: "Bearer " + token 
  )
  .then(response => 
    commit("SET_USERINFO", response.data);
  );

如果我直接在浏览器中输入https://test-vue-backend.io/getroute?lang=nl,它可以工作。

我已经检查了 REST API service when called from azure APIM returning empty response body with Status code 200 和 Azure API Management returning 200 status without response body 但这两个似乎都与我的问题无关,因为在我的顶层(所有 api)上启用了转发请求策略并且我没有策略。

编辑: 感谢下面的 cmets,我发现如果我从入站和出站部分中删除 &lt;/base&gt;,CORS 就可以工作。 但是,我不明白基地的原因是什么。

所有 API 的子句如下所示:

<policies>
    <inbound>
        <cors allow-credentials="true">
            <allowed-origins>
                <origin>https://other-host-than-the-one-i-need.net</origin>
            </allowed-origins>
            <allowed-methods preflight-result-max-age="300">
                <method>*</method>
            </allowed-methods>
            <allowed-headers>
                <header>*</header>
            </allowed-headers>
            <expose-headers>
                <header>*</header>
            </expose-headers>
        </cors>
    </inbound>
    <backend>
        <forward-request />
    </backend>
    <outbound />
    <on-error />
</policies>

我最好的猜测是它只允许基本来源,即使在我的来源之前提到了&lt;/base&gt; 标签,但我不确定。

【问题讨论】:

对您正在使用的产品运行“计算有效策略”以进行单次操作并将其粘贴到此处。它显示了什么?在应用 cors 之前是否执行了任何策略? 预检 OPTIONS 请求的响应正文为空,这是意料之中的。它不会引起任何问题。但实际上导致问题的是响应缺少 Access-Control-Allow-Origin 响应标头和 Access-Control-Allow-Methods 响应标头和 Access-Control-Allow-Headers 响应标头。 @SteppingRazor 从我现在可以看到的情况来看,该 API 未在任何产品下列出。这是一个问题吗?如何在应用 cors 之前检查是否执行了任何策略? @sideshowbarker 我明白了。但是我不明白为什么没有响应标头,因为我已经在出站部分配置了它们。 我只能推测,虽然出站部分中的配置可能会导致响应标头添加到 POST 和 GET 响应中,但它不会导致响应标头添加到 OPTIONS 响应中。服务器系统通常对 OPTIONS 请求和响应进行特殊处理,这些处理与处理 GET 和 POST 的代码是分开的。所以我猜预检 OPTIONS 请求被其他一些代码捕获和处理,因此永远不会到达问题中引用的代码。因此,您可能想尝试对该服务器代码进行一些跟踪以查看 【参考方案1】:

您的政策解释了这一点。在请求生命周期内只会调用单个 CORS 策略 - 在处理期间遇到的第一个策略。因此,由于来源不匹配,因此对您的 OPTIONS 请求产生空的 200 响应是全局级别的策略。你有两个选择:

    确保每个请求管道中只有一个策略 在 CORS 策略上使用 terminate-unmatched-request 属性来控制如果请求与任何指定的来源都不匹配,应如何处理。

【讨论】:

以上是关于Azure APIM 在预检和从 axios 发出的 GET 请求时返回空响应正文,状态代码为 200的主要内容,如果未能解决你的问题,请参考以下文章

使用 Azure 应用网关 + APIM 进行 SSL 固定

从 APIM 策略获取 Azure 表存储实体

发出帖子请求时出现Vuejs axios错误

Azure APIM 网关正在修剪 api url 中的尾部斜杠

如何使用 Azure APIM 创建 OCSP 请求并验证响应?

Azure APIM 定价层和虚拟网络