使用create-react-app和webpack进行CORS API请求 - 不需要express

4
我想在不禁用浏览器的CORS的情况下进行API调用。该应用程序是使用react-create-app创建的,它使用webpack和webpackDevServer进行开发。在这里找到的所有答案都建议将以下代码放入我的webpack.config文件中。
devServer: {
      headers: {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
        "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
      }
  }

然而,我已经在许多不同的地方尝试过它,但它并没有起作用。我一直收到错误消息:
“无法加载资源:服务器响应了500()状态”
还有:
“无法加载……预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,源'http://localhost:3000'不能访问。响应的HTTP状态代码为500。”
我还试图将我的axios调用更改为fetch调用。
return fetch(process.env.REACT_APP_API_URL + "/prod/user", {
    mode: 'no-cors',
    credentials: 'include',
    method: "GET",
    headers: {
        "x-api-key": process.env.REACT_APP_API_KEY
    }
})
.then(response => {
   return response.json()
})
.then(resp => dispatch(receiveUsers(resp.data)))
.catch(error => {console.log(error)})

那让我更接近了,但控制台现在返回的是 Response {type: "opaque", url: "", redirected: false, status: 0, ok: false, …} 如何解决这个跨域问题?感谢您的帮助! 响应/请求头

CORS是一项安全功能,理想情况下不应该被避免。你有API源代码的访问权限吗?如果有,你需要允许你正在开发的URL。Origin 'http://localhost:3000' is therefore not allowed access. 因此,添加localhostlocalhost:3000。如果你没有访问权限,那么你需要代理请求。 - Sebastijan Dumančić
3个回答

3

无法添加评论,因此我将把它作为答案。

如果您正在尝试访问由另一个节点应用程序或来自另一个主机提供的 API,则可以配置 webpack-dev-server 将该请求代理到实际服务器。

有关详细信息,请查看Create React App 文档:代理开发中的 API 请求


链接已更改。新链接为:https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development - Rick
我曾经遇到过同样的问题,而这个代理路由是最简单的解决方案。只需要在你的 package.json 文件中添加一行代码(例如:"proxy": "http://localhost:8090"),就可以在开发模式下(npm start)将 API 调用路由到另一个主机。 - cakidnyc

1
我看到预检OPTIONS请求返回500状态码,这让我想到可能是Web服务器不允许使用OPTIONS请求方法。很多Web服务器默认只允许HEAD、GET和POST请求,所以这应该是第一件需要检查的事情。
如果这没有帮助,确保上述webpack.config代码对所有请求都运行(即对OPTIONS、GET和POST请求都适用)。
如果这还没有帮助,请将完整的请求和响应头发布到OPTIONS和GET请求中,并让我们看看接下来会发生什么。

这个很有帮助,谢谢。我调用的API确实允许使用*,而且我之前对POST和GET的调用方式是错误的。最后发现,我需要指定content-type为"application/x-www-form-urlencoded"来进行POST,并将数据转换为字符串以便发送到API。现在我可以进行GET和POST请求了,但是我的PUT请求因为被发送为OPTIONS而被CORS拦截,虽然返回了200状态码,但未通过预检查的访问控制检查。我应该能够发送PUT请求,不确定为什么不起作用。也许是我使用axios发送请求的方式有问题? - Tim
更正。PUT 方法总是会让浏览器发出一个预检 OPTIONS 请求。现在的问题是,为什么当 API 允许跨域资源共享时,浏览器会阻止 PUT 请求。 - Tim
一个 PUT 请求将导致浏览器首先发送预检查 OPTIONS 请求。如果您没有正确响应该 OPTIONS 请求的 CORS 标头,那么浏览器甚至不会发出后续的 PUT 请求。你能发布 OPTIONS 请求的请求/响应标头吗? - roryhewitt
嘿,Rory,谢谢 - 我在原始帖子中添加了一个图像链接。 - Tim
好的,所以您需要在OPTIONS请求和PUT请求中包含CORS响应头。目前还没有发生这种情况。我怀疑您的webpack.config定义没有用于OPTIONS请求,但我不知道您的代码是什么。但我看到现在您已经收到了OPTIONS请求的200响应,所以自您首次发布问题以来肯定有些变化。 - roryhewitt
需要调整API以容纳OPTIONS请求。感谢您的帮助@roryhewitt - Tim

1
最好的选择是使用代理,特别是当您使用外部API或多个API时。只需安装proxy-middleware并创建文件src/setupProxy.js,在文件内添加类似于以下代码即可:
const proxy = require('proxy-middleware');

module.exports = app => {
  // setup proxies
  // 1st api
  app.use("/etherscan", proxy("http://api.etherscan.io/api"));
  // 2nd api
  app.use("/cryptocompare", proxy("https://min-api.cryptocompare.com/data"));

  // Note: setupProxy is an express server so you can also override anything in the req or res before proxy them for example 
  app.use("/cryptocompare", (req,res,next) => {
     req.headers = { 
        ...req.headers,
        "my-header":"my header value"
     }
     return proxy("https://min-api.cryptocompare.com/data")(req,res,next);
  });

  app.use("/cryptocompare", proxy("https://min-api.cryptocompare.com/data"));


  // also it's better to use .env variables
  // const {REACT_APP_API_ETHERSCAN_PROXY, REACT_APP_API_ETHERSCAN_BASE_URL} = process.env;
  // app.use(REACT_APP_API_ETHERSCAN_PROXY, REACT_APP_API_ETHERSCAN_BASE_URL);      
};

对于您的API,最好能够从.env变量中获取基本URL,例如:

const {REACT_APP_API_ETHERSCAN_PROXY, REACT_APP_API_ETHERSCAN_BASE_URL} = process.env;
const baseURL = REACT_APP_API_ETHERSCAN_PROXY || REACT_APP_API_ETHERSCAN_BASE_URL;

额外建议:不要在每个API调用中都保持传递process.env.REACT_APP_API_URL和headers,而是可以查看axios,您可以创建一个具有默认属性的API实例,例如:

const {
  REACT_APP_API_ETHERSCAN_PROXY, 
  REACT_APP_API_ETHERSCAN_BASE_URL,
  REACT_APP_API_TIMEOUT,
  REACT_APP_API_ETHERSCAN_KEY
} = process.env;

export const etherscanAPI = axios.create({
  "baseURL": REACT_APP_API_ETHERSCAN_PROXY || REACT_APP_API_ETHERSCAN_BASE_URL,
  "timeout": parseInt(REACT_APP_API_TIMEOUT),
  "headers": {
    "X-Requested-With": "XMLHttpRequest"
  },
  "params": {
    "apiKey": REACT_APP_API_ETHERSCAN_KEY
  }
});

然后 etherscanAPI.get("/whatever")

我该如何在setupProxy中发送头部信息? - Catarina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接