使用 React 在 Front 调用 API 时出错

Posted

技术标签:

【中文标题】使用 React 在 Front 调用 API 时出错【英文标题】:Error when calling API in Front using React 【发布时间】:2020-04-10 06:01:31 【问题描述】:

当我尝试单击按钮进行订阅时显示错误。这是浏览器控制台中的错误消息:

从源“http://localhost:3000”获取“https://api.exhia.com/api/accounts/addaccount”的访问权限已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:没有“Access-Control-Allow-Origin”标头出现在请求的资源上。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。

这是处理点击的代码示例:

 handleFormSubmit(event) 
    event.preventDefault();
    this.setState( msg: 'checked' );
    const validation = this.validator.validate(this.state);
    this.setState( validation );
    this.submitted = true;
    if (
      this.state.checked === true &&
      validation.subConfirm_pwd.isInvalid === false
    ) 
      fetch(`$API/api/accounts/addaccount`, 
        method: 'POST', // 'GET', 'PUT', 'DELETE'
        body: JSON.stringify(
          email: this.state.sub_mail,
          password: this.state.sub_pwd,
          acceptCGU: this.state.checked,
        ),
        headers: 
          'Content-Type': 'application/json;charset=utf-8',
          Accept: 'application/json',
        ,
      )
        .then(res => res.json())

.....

我正在努力解决这个问题,这是我的 webpack.config:

const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const  CleanWebpackPlugin  = require('clean-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = 
    mode: 'production',
    entry: './src/index.js',
    output: 
        path: path.resolve(__dirname, 'build'),
        publicPath: '/',
        filename: 'bundle.js'
    ,
    devServer: 
        contentBase: path.join(__dirname, './build'),
        compress: true,
        port: 3000,
        historyApiFallback: true,
    ,
    performance: 
        hints: false,
        maxEntrypointSize: 512000,
        maxAssetSize: 512000
    ,
    module: 
      rules: [
        
          test: /\.(css|sass|scss)$/,
          use: [
            MiniCssExtractPlugin.loader,
            
              loader: 'css-loader',
              options: 
                sourceMap: true,
                importLoaders: 2
              ,
            ,
            
              loader: 'sass-loader',
              options: 
                sourceMap: true,
              ,
            ,
          ],
        ,
        
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: 
            loader: "babel-loader"
          
        ,
        
          test: /\.(png|svg|jpg|gif)$/,
          use: ["file-loader"]
        ,
        
            test: /\.(woff(2)?|ttf|otf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
            use: [
              
                loader: 'file-loader',
                options: 
                  name: '[name].[ext]',
                  outputPath: 'fonts/'
                
              
            ]
        
      ]
    ,
     //remove comments from JS files
    optimization: 
        minimizer: [
        new UglifyJsPlugin(
            uglifyOptions: 
            output: 
                comments: false,
            ,
            ,
        ),
        new OptimizeCSSAssetsPlugin(
            cssProcessorPluginOptions: 
            preset: ['default',  discardComments:  removeAll: true  ],
            
        )
        ],
    ,
    plugins: [
        new MiniCssExtractPlugin(
            filename: "[name].css"
        ),
        new ManifestPlugin(),
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin(
            template: path.resolve('./public/index.html'),
        ),
    ]
;

我不确定错误来自哪里。我该如何解决?

【问题讨论】:

可能重复:***.com/questions/20035101/… - 本质上您正在执行跨域请求,因此如果远程服务器在您的控制之下,您必须代理或更改远程服务器。 你好 Squiggs,谢谢你的回答。请问我怎么能用代理来处理呢?因为我是服务器的管理员,但我不知道如何按照你说的那样更改远程 对于站点 B 想让站点 A 访问的每个资源/页面,站点 B 应该为其页面提供响应标头: Access-Control-Allow-Origin: siteA.com 所以,在你的在这种情况下,您将需要一个允许您在开发时使用本地主机的标头。 siteA 和 siteB 是什么意思?我只有一个站点:fatboar.extia.com :/ 您不要将该代码放在客户端或代码中的任何位置。它必须存在于您正在与之交谈的 API 中的服务器端。您提到您是“服务器管理员”——我认为这意味着您可以访问 API 的源代码。换句话说:api.exhia.com/api/accounts/addaccount 需要改变 【参考方案1】:

http://localhost:3000 已被 CORS 策略阻止,因此您只需将 HTTP 标头添加到为您的 API 提供服务的服务器即可。这意味着更改 API。

在您的情况下,这是 (fatboar.extia.com) 或 (api.exhia.com) - 我不知道是哪一个,因为您似乎在您的问题/cmets 中可以互换使用它们。

此站点提供了有关如何执行此操作的详细信息:https://enable-cors.org/server.html - 如果您需要进一步的指导,我将研究如何为您的特定平台添加标题。您应该能够在 Postman (https://www.getpostman.com/) 之类的工具中看到返回的标头 - 我将首先探索 API 以检查标头,然后在浏览器中继续执行您的代码。

1) Access-Control-Allow-Origin: http://localhost:3000

注意。当您转移到生产环境时,您可能需要更改上述内容以匹配进行调用的服务器。因此,例如,当您在本地主机上托管应用程序时本地:3000。假设您在 blah.com 上托管,则当您转移到生产环境时,您的访问控制标头也需要更改。

2) Access-Control-Allow-Origin: http://blah.com

不要被暗示通配符的答案所诱惑,例如

3) Access-Control-Allow-Origin: * 

这将允许任何授权人员从任何域访问 API,这可能不是您想要的。因此,任何站点都可以代表其访问者向您的站点发出请求并处理其响应。

【讨论】:

【参考方案2】:

这是因为您正在尝试调用在不同服务器上运行的服务。

More on cors issue

如果是开发环境,你应该使用某种代理,例如 webpack Dev server proxy

另一种选择是通过某种插件禁用浏览器中的cors签入,在chrome应用商店中简单搜索会给你很多插件。

【讨论】:

你好 Squiggs,谢谢你的回答。它在 prod 而不是 dev 上,请查看上面 Edit 中的 webpack.config 你说我需要禁用浏览器中的 cors 检查,但每个人都不会这样做:/ 禁用 cors 和代理是我们通常在开发环境中所做的事情。在生产环境中,API 服务器应该允许主机配置或所有应用程序应该托管在同一个域中,我建议请参阅 cors 文档这里是一个很好的解释***.com/questions/36958999/… 嘿 Nirjhar,不幸的是它并没有真正帮助我解决我的问题,它只是解释了交叉的概念..

以上是关于使用 React 在 Front 调用 API 时出错的主要内容,如果未能解决你的问题,请参考以下文章

api 请求在 react-native 中不起作用

调用 AWS API Gateway Private 无法从 Front 访问,但 EC2 有效

在 React 中由 Axios 调用时 API 中的 PHP-Session 发生变化

使用 React 应用程序调用 REST API 时出错

在 REACT 中进行 API 调用时动态设置目标

React 中的竞争条件; API 调用、数据库调用