要点
由于 AWS CloudFront 发行版的域名被用作 API 的基本 URL,我的 React Web 应用程序的 API 请求失败,状态为 403(禁止)。在浏览器的开发控制台中打开“网络”选项卡,然后尝试登录我的应用程序,我看到请求 URL 如下:
`https://[my_cf_distribution_link]/[my_api_url]/login`
当然应该是:
`https://[my_api_url]/login`
一些额外信息
我的 AWS 管道是使用 CodeBuild、S3 和 CloudFront 设置的。当我在 S3 存储桶上启用静态托管并尝试从存储桶的域登录时,一切都会按预期进行。我尝试过更改 CF 发行版上的各种设置,例如查看器协议行为,但没有任何效果。据我所知,我没有使用 API Gateway 或 Lambda。另外,需要澄清的是,我的 S3 存储桶可以通过适当的存储桶策略公开访问,并且 CF 的原始访问设置也设置为公开。网站内容本身通过 CF 域链接完美地提供服务,实际上只是 API 请求失败了。
我在 React 应用程序中使用 axios 来处理 API 请求。我附上了一些负责发送请求的代码的屏幕截图、URL 和配置的运行时值的浏览器控制台输出以及 API 请求标头。为了保护敏感信息已被删除
不确定这是否会对遇到类似问题的人有所帮助,因为我的具体情况的解决方案在于实现 axios 库来请求 API 调用,并且与任何特定的 AWS 配置无关。
罪魁祸首
在我的 React 应用程序中,我有一个类“BaseController”来处理我的所有 API 调用。在类的构造函数中,我从“.env”文件中读取后端的基本 API URL,并将其存储在常量字段中。当发出 API 请求时,我获取 API 的端点(例如“/login”)并将其附加到基本 URL,然后将结果传递给 axios。
import axios from "axios";
const BASE_URL = process.env.REACT_APP_BASE_URL;
export default class BaseController {
constructor() {
this.BASE_URL = BASE_URL;
}
sendRequest = async (method, url, body) => {
let config = {
method: method
};
url = `${this.BASE_URL}/${url}`;
// Remove any duplicate '/'
url = url.replace(/\/+/g, "/");
if (params) {
config.params = this.addQueryParameters(url, params);
}
if (body !== null) {
config.data = JSON.stringify(body);
}
const response = await axios(url, config);
return response.data;
};
}
但是,传递给 axios 的 URL 不会被读取为它应该使用的绝对 URL。因此,当从 CloudFront 域发出请求时,axios 使用 CF 域作为其基本 URL。为什么在开发过程中我的本地计算机上甚至在静态托管的 S3 存储桶域中没有发生这种情况,我不知道。
解决方案
正确设置 axios 的默认基本 URL,如下所示:
axios.defaults.baseURL = baseURL;
所以上面的函数看起来像这样:
import axios from "axios";
const BASE_URL = process.env.REACT_APP_BASE_URL;
export default class BaseController {
constructor() {
axios.defaults.baseURL = BASE_URL;
}
sendRequest = async (method, url, body) => {
let config = {
method: method
};
// Remove any duplicate '/'
url = url.replace(/\/+/g, "/");
if (params) {
config.params = this.addQueryParameters(url, params);
}
if (body !== null) {
config.data = JSON.stringify(body);
}
const response = await axios(url, config);
return response.data;
};
}
然后就可以像这样使用(非常简化的使用示例):
import BaseController from "./BaseController";
const response = await BaseController.sendRequest("POST", "/login", credentials);
希望这对某人有帮助!