已解决 - CORS、OPTIONS 和 PreFlight 问题 在 Vue.js 中使用 Axios 使用 REST API

Posted

技术标签:

【中文标题】已解决 - CORS、OPTIONS 和 PreFlight 问题 在 Vue.js 中使用 Axios 使用 REST API【英文标题】:SOLVED - CORS, OPTIONS and PreFlight problems Consuming REST API with Axios in Vue.js 【发布时间】:2019-11-17 10:15:53 【问题描述】:

TL;DR

我的 Axios 调用的格式存在一些问题,发现在调用中使用标头作为 const 或 var 不起作用,而将标头直接转储到调用中确实有效。

php 方面,Access-Control-Allow-Headers 指令中有一些额外的大括号导致标头响应被括起来。

总而言之,通过浏览器开发工具中的“网络”选项卡进行故障排除,帮助我们找到了解决方案。

我对一般的编程相当陌生,对 Vue.js 和 Axios 也很陌生。所以我可能在概念上和句法上都遗漏了一些片段。我正在开发一个运行自定义管理工具的基于 PHP 的应用程序的前端。我的目标是使用 Vue.js 构建前端,并专门使用 Axios 进行 API 调用。

我正在使用具有公共端口转发的 Lightsail 服务器。 安装了远程开发工具并通过 SSH 连接的 Visual Studio Code,以便在该服务器而不是我的机器上进行开发。 Vue CLI 3 来设置我的项目并选择 Babel 和 Linting 的默认包。 Axios 是通过 npm 安装的。

我们的 PHP 环境最初允许我传递用户 ID、API 密钥和查询等参数(而 user = x method=get, apikey=x, ),我可以轻松地使用数据并将其输出到 v-for一切都很好。但是,它不是一个设计良好的 API 结构,所以我们改变了传递参数的想法,因为我不喜欢在 URL 中传递 API 密钥,也不喜欢必须传递 SQL 查询来获取的想法数据。所以,我的同事调整了 API,所以现在我们有了一个类似于 https://tunnel.host.com/api/sites/read.php 的 URL。稍后我们还将为其余的 CRUD 操作提供 PHP 文件。但我需要克服我目前的问题。

我的研究立即让我想到了 CORS 问题,我花了很多时间阅读该主题,最终觉得这是阻止我将必要的标头传递给服务器并获得访问权限的问题。

我想了一会儿,安装 CORS npm 包会有所帮助,但这似乎只是为了解决本地托管服务器环境的问题。 (比如在开发环境中使用 ExpressJS 作为服务器)

阅读有关 CORS 的 Mozilla 文档后,我想知道是否需要在 OPTION HTTP 请求中发送预检标头。

到目前为止,我已经尝试过: - 添加带有开发服务器选项的 vue.config.js 文件(我将在下面包含代码) - 使用 POSTMAN 构造标头并传递 GET 请求 - 效果很好 - 尝试在 Axios 对象中使用 headers 键(代码如下)

我的负责 PHP 方面的同事向我保证文件中的所有 CORS 标头都是正确的。

我只有一个组件被加载到 App.vue 中,称为 AxiosTest。

我编辑了这篇文章以更新我的发现。

通过将标头作为 const 发送,请求将作为 GET 进行

const config = 
            headers: 
                    "content-type": "application/vnd.api+json",
                    "Cache-Control": "no-cache",
                    "x-api-key": "9xxxxxxxxxxxxxxxxxxxxxx9"
            
        
        axios.get(
            `https://tunnel.xxxxx.com/api/headers.php?`, config
            )
            .then(response => 
                this.results = response;
            )
            .catch(error => 
                // eslint-disable-next-line
                console.log(error)
            )

以及头部响应

HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Mon, 08 Jul 2019 19:22:55 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: http://54.x.x.155:8080
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 0

和请求

Host: tunnel.xxxxxx.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://54.x.x.155:8080/
Origin: http://54.x.x.155:8080
DNT: 1
Connection: keep-alive
Cache-Control: max-age=0

但是,如果我将 headers 对象保留在 axios.get 函数中,我会将其作为 OPTIONS 发送

axios.get(
            `https://tunnel.xxxx.com/api/headers.php?`,
                headers: 
                    "content-type": "application/vnd.api+json",
                    "Cache-Control": "no-cache",
                    "x-api-key": "9xxxxxxxxxxxxxxxxxxxxxx9"
                
            )
            .then(response => 
                this.results = response;
            )
            .catch(error => 
                // eslint-disable-next-line
                console.log(error)
            )

回应

HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Mon, 08 Jul 2019 19:22:55 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: http://54.x.x.155:8080
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 0
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: cache-control,x-api-key

请求

Host: tunnel.xxxxx.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: cache-control,x-api-key
Referer: http://54.x.x.155:8080/
Origin: http://54.x.x.155:8080
DNT: 1
Connection: keep-alive
Cache-Control: max-age=0

vue.config.js

module.exports = 
    devServer: 
        public: '54.x.x.x:8080',
        proxy: 'https://tunnel.xxxxxxx.com/'
    
  

在最成功的测试中,我仍然收到 Invalid CORS origin 或 invalid API key。

任何人可以分享的任何提示、代码 sn-ps、链接或经验将不胜感激。

【问题讨论】:

如果我尝试调试它,我会将注意力集中在浏览器开发工具的 Network 选项卡上。找到您尝试发出的请求并仔细研究请求和响应中的标头。大概它正在发送一个 OPTIONS 请求(从您的 GET 请求自动生成),但是该请求是否具有您期望的所有标头?检查响应标头,它们看起来是否正确?你没有在问题中包含任何这些信息,所以我很难进一步了解这一点。隧道/代理让我想知道是否有中间人在干扰。 谢谢 Skirtle,我肯定一直在使用开发工具中的网络选项卡。以下是页面加载或刷新期间显示请求和响应的标头。 “阅读有关 CORS 的 Mozilla 文档后,很明显我需要在 OPTION HTTP 请求中发送预检标头。” — 不会。浏览器会自动设置 感谢@Quentin 的帮助,这对我来说是全新的,因此非常感谢您的意见。我想是因为我发送了一个简单请求,即只是一个 GET 请求,浏览器不会发送预检标头。但是我将如何在我的 Axios 请求中发送我的 x-api-key 标头? 将您的回复格式化为代码,以便它们可读。几乎不可能说出所有空白都被触发的情况。仅包括由 Ajax 发送和接收的那些。包括请求和响应主体。在控制台中引用与它们相关的错误消息。 【参考方案1】:

我的 Axios 调用的格式存在一些问题,发现在调用中使用标头作为 const 或 var 不起作用,而将标头直接转储到调用中确实有效。

在 PHP 方面,Access-Control-Allow-Headers 指令中有一些额外的大括号导致标头响应被括起来。

总而言之,通过浏览器开发工具中的“网络”选项卡进行故障排除,帮助我们找到了解决方法。

【讨论】:

【参考方案2】:

我还有另一个测试脚本来回显所有 $_SERVER 数组值,当他的 Axios 调用它时,我从未看到任何自定义标头。但是,如果您从命令行使用 curl 调用事物(添加标题),它们肯定会出现在结果输出中。

<?
//---------------------------------------------------------------------------------------------------
include("../dashboard/subs.php");
//---------------------------------------------------------------------------------------------------
if (! IS_SSL()) 
  echo("\"message\":\"API requires all traffic to be SSL encrypted.\"\n");
  exit;

//---------------------------------------------------------------------------------------------------
InitCors();
//---------------------------------------------------------------------------------------------------
  $JSON  = "\"data\":[";
  $JSON .= json_encode($_SERVER);
  $JSON .= "]";
  echo("$JSON\n");
//---------------------------------------------------------------------------------------------------
?>

【讨论】:

问题已解决,事实证明***.com/questions/8719276/… 的 PHP CORS 示例在 curly backets 中包含响应标头是不正确的。删除这些后,Axios 开始正常工作。 我可以确认。谢谢拉里。【参考方案3】:

这是我在 PHP 脚本顶部调用的函数,以允许请求者 100% 完全访问 CORS。

function InitCors() 
  if (isset($_SERVER["HTTP_ORIGIN"])) 
    header("Access-Control-Allow-Origin: $_SERVER["HTTP_ORIGIN"]");
    header("Access-Control-Allow-Credentials: true");
    header("Access-Control-Max-Age: 0");
  
  if ($_SERVER["REQUEST_METHOD"] == "OPTIONS") 
    if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_METHOD"])) header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
    if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"])) header("Access-Control-Allow-Headers: " . $_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"] ."");
  
  header("Content-Type: application/json; charset=UTF-8");

【讨论】:

【参考方案4】:

看the documentation for axios:

axios.get(url[, config])

get 方法接受 两个 参数,但您传递了 三个

因此,您尝试传递的数据被视为配置(并被忽略,因为没有一个值是有效的配置选项)并且配置数据(包括请求标头对象)被忽略。

查询字符串必须是 URL 的一部分。 Axios 不会使用第二个参数为您生成它。

const data = 
    foo: "bar"
;
axios.get(
    `https://example.com/api/headers.php?$new URLSearchParams(data)`, 
        headers: 
            "Cache-Control": "no-cache",
            "content-type": "application/vnd.api+json",
            "x-api-key": "9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9"
        
    ).then(response => 
        console.log(response);
    ).catch(error => 
        console.log(error)
    )

【讨论】:

以上是关于已解决 - CORS、OPTIONS 和 PreFlight 问题 在 Vue.js 中使用 Axios 使用 REST API的主要内容,如果未能解决你的问题,请参考以下文章

在 .NET Core Web API 上为 CORS 启用 OPTIONS 标头

OPTIONS 方法在跨域请求(CORS)中的应用

HTTP OPTIONS 飞行前请求加载已取消,Chrome 中处于待处理状态

Vue报错:OPTIONS 405 Method Not Allowed以及CORS跨域错误

ASP.NET Core Cors 已启用但无法正常工作

Domino、CORS 和 OPTIONS 请求