尝试从REST API获取数据时,请求的资源上没有“ Access-Control-Allow-Origin”标头

2020/09/24 10:21 · javascript ·  · 0评论

我正在尝试从HP Alm的REST API中获取一些数据。它与一个小的curl脚本一起使用时效果很好-我得到了数据。

现在使用JavaScript进行操作,获取和ES6(或多或少)似乎是一个更大的问题。我不断收到此错误消息:

提取API无法加载。对预检请求的响应未通过访问控制检查:在所请求的资源上不存在“ Access-Control-Allow-Origin”标头。因此,不允许访问源' http://127.0.0.1:3000 '。响应的HTTP状态代码为501。如果不透明的响应满足您的需求,请将请求的模式设置为“ no-cors”以在禁用CORS的情况下获取资源。

我了解这是因为我试图从本地主机中获取数据,并且解决方案应使用CORS。现在我以为我确实这样做了,但是不知何故它要么忽略了我在标题中写的内容,要么是其他问题?

那么,是否存在实施问题?我做错了吗?我无法检查服务器日志。我真的有点卡在这里。

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

我正在使用Chrome。我也尝试使用该Chrome CORS插件,但是随后出现另一条错误消息:

当请求的凭据模式为“ include”时,响应中“ Access-Control-Allow-Origin”标头的值不得为通配符“ *”。因此,不允许访问源' http://127.0.0.1:3000 '。XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制。

该答案涉及很多领域,因此分为三个部分:

  • 如何使用CORS代理来解决“无访问权限-允许-来源标头”问题
  • 如何避免CORS飞行前
  • 如何解决“ Access-Control-Allow-Origin标头一定不能为通配符”的问题

如何使用CORS代理来解决“无访问权限-允许-来源标头”问题

如果您不控制服务器,则您的前端JavaScript代码正在向其发送请求,并且该服务器的响应问题仅在于缺少必要的Access-Control-Allow-Origin标头,那么您仍然可以使事情正常进行-通过通过CORS代理。为了展示它是如何工作的,首先这里是一些不使用CORS代理的代码:

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

造成该catch区块被打的原因是,浏览器阻止该代码访问从返回的响应https://example.com而浏览器这样做的原因是,响应缺少Access-Control-Allow-Origin响应头。

现在,这是完全相同的示例,只是在其中添加了CORS代理:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

注意:如果在尝试https://cors-anywhere.herokuapp.com时关闭或不可用,请参见下文,了解如何在2-3分钟内在Heroku上部署自己的CORS Anywhere服务器。

上面的第二个代码段可以成功访问响应,因为采用请求URL并将其更改为https://cors-anywhere.herokuapp.com/https://example.com(只需在其前面加上代理URL)即可请求通过该代理取得,然后:

  1. 将请求转发给https://example.com
  2. 收到来自的回复https://example.com
  3. Access-Control-Allow-Origin标头添加到响应中。
  4. 将带有添加的标头的响应传递回请求的前端代码。

然后,浏览器允许前端代码访问响应,因为带有Access-Control-Allow-Origin响应标头响应就是浏览器看到的内容。

您可以使用https://github.com/Rob--W/cors-anywhere/中的代码轻松运行自己的代理

您还可以使用5条命令在2-3分钟内轻松地将自己的代理部署到Heroku中:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

运行这些命令后,您将最终在以下位置运行自己的CORS Anywhere服务器,例如https://cryptic-headland-94862.herokuapp.com/因此,不要在您的请求URL前面加上https://cors-anywhere.herokuapp.com,而是在您自己的实例的URL 前面加上前缀;例如https://cryptic-headland-94862.herokuapp.com/https://example.com

因此,如果您尝试使用https://cors-anywhere.herokuapp.com时发现它已关闭(有时会关闭),那么可以考虑获取一个Heroku帐户(如果您还没有的话),并获取2或花费3分钟完成上述步骤,以在Heroku上部署您自己的CORS Anywhere服务器。

无论您是运行自己的网站还是使用https://cors-anywhere.herokuapp.com或其他开放式代理,即使该请求触发了浏览器执行CORS预检OPTIONS请求,该解决方案仍然有效–因为在这种情况下,代理还会发回使预检成功所需Access-Control-Allow-HeadersAccess-Control-Allow-Methods标头。


如何避免CORS飞行前

问题中的代码由于发送了Authorization标头而触发了CORS预检

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

即使没有这些,Content-Type: application/json标题也将触发预检。

“预检”的含义是:在浏览器尝试输入POST问题代码之前,它将首先向OPTIONS服务器发送请求-确定服务器是否选择接收POST包含AuthorizationContent-Type: application/json标头的跨域

它与一个小的curl脚本一起使用时效果很好-我得到了数据。

要使用进行正确的测试curl,您必须模拟OPTIONS浏览器发送的预检请求:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

https://the.sign_in.url替换为您的实际sign_in网址。

浏览器需要从该OPTIONS请求中看到的响应必须包含以下标头:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

如果OPTIONS响应中不包含这些标头,则浏览器将在那里停止,甚至从不尝试发送POST请求。另外,响应的HTTP状态代码必须为2xx,通常为200或204。如果是其他任何状态代码,浏览器将在那里停止。

问题中的服务器正在OPTIONS使用501状态代码来响应请求,这显然意味着它试图表明它未实现对OPTIONS请求的支持在这种情况下,其他服务器通常会以405“不允许使用方法”状态代码进行响应。

因此POST,如果服务器OPTIONS使用405或501或200或204以外的任何其他值响应该请求,或者如果未响应必要的响应,则永远无法从前端JavaScript代码直接向该服务器发出请求响应头。

避免触发该问题的事前准备的方法是:

  • 如果服务器不需要Authorization请求标头,而是(例如)依赖于嵌入在POST请求正文中或作为查询参数的身份验证数据
  • 如果服务器不要求POST主体具有Content-Type: application/json媒体类型,而是使用名为(或任何其他形式)的参数(其值为JSON数据)接受POST主体application/x-www-form-urlencodedjson

如何解决“ Access-Control-Allow-Origin标头一定不能为通配符”的问题

我收到另一条错误消息:

当请求的凭据模式为“ include”时,响应中“ Access-Control-Allow-Origin”标头的值不得为通配符“ *”。因此,不允许访问来源“ http://127.0.0.1:3000”。XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制。

对于包含凭据的请求,如果Access-Control-Allow-Origin响应标头的值为,浏览器将不允许您的前端JavaScript代码访问响应*相反,在这种情况下,该值必须与您的前端代码的来源完全匹配http://127.0.0.1:3000

请参阅MDN HTTP访问控制(CORS)文章中的凭据请求和通配符

如果控制要向其发送请求的服务器,那么处理这种情况的一种常用方法是将服务器配置为采用Origin请求标头的值,并将其回显/反射回Access-Control-Allow-Origin响应标头的值例如,使用nginx:

add_header Access-Control-Allow-Origin $http_origin

但这只是一个例子。其他(网络)服务器系统提供了类似的方法来回显原始值。


我正在使用Chrome。我也尝试过使用Chrome CORS插件

Chrome CORS插件显然只是将Access-Control-Allow-Origin: *标头注入浏览器看到的响应中。如果插件更聪明,它将将假Access-Control-Allow-Origin响应标头的值设置为前端JavaScript代码的实际来源http://127.0.0.1:3000

因此,即使进行测试,也请避免使用该插件。这只是分心。如果您想测试从服务器获得的响应,而没有浏览器对其进行过滤,则最好使用curl -H上述方法。


至于fetch(…)问题中请求的前端JavaScript代码

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

删除这些行。Access-Control-Allow-*头是响应头。您永远不想在请求中发送它们。唯一的效果就是触发浏览器进行预检。

当客户端URL和服务器URL不匹配(包括端口号)时,将发生此错误。在这种情况下,您需要为CORS启用服务,即跨源资源共享。

如果您要托管Spring REST服务,则可以在博客文章“ Spring Framework中的CORS支持”中找到它

如果您正在使用Node.js服务器托管服务,则

  1. 停止Node.js服务器。
  2. npm install cors --save
  3. 将以下行添加到您的server.js

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration

出现问题是因为您在前端中添加了以下代码作为请求标头:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

这些标头属于response,而不是request。因此删除它们,包括以下行:

headers.append('GET', 'POST', 'OPTIONS');

您的请求有'Content-Type: application/json',因此触发了所谓的CORS预检。这导致浏览器使用OPTIONS方法发送了请求。有关详细信息,请参见CORS预检

因此,在后端,您必须通过返回以下响应标头来处理此预检请求:

Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, OPTIONS
Access-Control-Allow-Headers : Origin, Content-Type, Accept

当然,实际语法取决于您在后端使用的编程语言。

在您的前端,应该像这样:

function performSignIn() {
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));
    headers.append('Origin','http://localhost:3000');

    fetch(sign_in, {
        mode: 'cors',
        credentials: 'include',
        method: 'POST',
        headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

就我而言,我使用以下解决方案

前端或角度

post(
    this.serverUrl, dataObjToPost,
    {
      headers: new HttpHeaders({
           'Content-Type':  'application/json',
         })
    }
)

后端(我使用php)

header("Access-Control-Allow-Origin: http://localhost:4200");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header("Access-Control-Allow-Headers: Content-Type, Authorization");

$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
print_r($request);

只是我的两分钱...关于如何使用CORS代理来解决“无Access-Control-Allow-Origin标题”问题

对于那些在后端使用php的人来说,部署“ CORS代理”非常简单:

  1. 创建一个名为“ no-cors.php”的文件,其内容如下:

    $URL = $_GET['url'];
    echo json_encode(file_get_contents($URL));
    die();
  2. 在前端,执行以下操作:

    fetch('https://example.com/no-cors.php' + '?url=' + url)
      .then(response=>{*/Handle Response/*})`

删除此:

credentials: 'include',

使用dataType: 'jsonp'对我有用。

   async function get_ajax_data(){
       var _reprojected_lat_lng = await $.ajax({
                                type: 'GET',
                                dataType: 'jsonp',
                                data: {},
                                url: _reprojection_url,
                                error: function (jqXHR, textStatus, errorThrown) {
                                    console.log(jqXHR)
                                },
                                success: function (data) {
                                    console.log(data);

                                    // note: data is already json type, you
                                    //       just specify dataType: jsonp
                                    return data;
                                }
                            });


 } // function               

我正在使用Spring REST,并解决了将AllowedMethods添加到WebMvcConfigurer中的问题。

@Value( "${app.allow.origins}" )
    private String allowOrigins;    
@Bean
public WebMvcConfigurer corsConfigurer() {
            System.out.println("allow origin: "+allowOrigins);
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**")
                    //.allowedOrigins("http://localhost")
                    .allowedOrigins(allowOrigins)
                    .allowedMethods("PUT", "DELETE","GET", "POST");
                }
            };
        }

就我而言,Web服务器阻止了“ OPTIONS”方法

检查您的Web服务器以了解选项方法

我正在使用“ Webtier”

/ www / webtier / domains / [域名] /config/fmwconfig/components/OHS/VCWeb1/httpd.conf

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

改成

<IfModule mod_rewrite.c>
  RewriteEngine off
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

如果在MacOS的开发环境中将NodeJS / Express用作后端,将ReactJS / axios用作前端,则需要在https下运行双方。以下是它最终对我有用的东西(经过数小时的深入潜水和测试):

步骤1:建立SSL凭证

只需按照5分钟内如何使HTTPS在本地开发环境上工作的步骤进行操作

您最终将获得几个文件,以用作运行https服务器和ReactJS Web的凭据:

server.key & server.crt

您需要将它们复制到前端和后端的根文件夹中(在生产环境中,您可能考虑将它们复制到./ssh中作为后端)。

步骤2:后端设定

我读了很多建议使用“ cors”软件包甚至设置(“ Access-Control-Allow-Origin”,“ *”)的答案,就像说:“欢迎黑客访问我的网站”。就是这样:

import express from 'express';
const emailRouter = require('./routes/email');  // in my case, I was sending an email through a form in ReactJS
const fs = require('fs');
const https = require('https');

const app = express();
const port = 8000;

// CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requests
app.all('*', (req, res, next) => {
    res.header("Access-Control-Allow-Origin", "https://localhost:3000");
    next();
});

// Routes definition
app.use('/email', emailRouter);

// HTTPS server
const credentials = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt')
};

const httpsServer = https.createServer(credentials, app);
httpsServer.listen(port, () => {
    console.log(`Back-end running on port ${port}`);
});

如果您要测试https是否可以正常运行,可以将httpsServer常量替换为以下内容:

https.createServer(credentials, (req: any, res: any) => {
  res.writeHead(200);
  res.end("hello world from SSL\n");
}).listen(port, () => {
  console.log(`HTTPS server listening on port ${port}...`);
});

然后从Web浏览器访问它:https:// localhost:8000 /

步骤3:前端设定

这是来自ReactJS前端的axios请求:

    await axios.get(`https://localhost:8000/email/send`, {
        params: {/* whatever data you want to send */ },
        headers: {
            'Content-Type': 'application/json',
        }
    })

现在,您需要使用我们已经创建的SSL凭证以https方式启动ReactJS网站。在MacOS终端中输入以下内容:

HTTPS=true SSL_CRT_FILE=server.crt SSL_KEY_FILE=server.key npm start

此时,您正在从前端的端口3000的https连接发送请求,后端的8000端口的https连接将接收请求。CORS应该对此感到满意;)

我在本地遇到此错误。我以管理员身份运行Visual Studio,并解决了该问题。

本文地址:http://javascript.askforanswer.com/changshicongrest-apihuoqushujushiqingqiudeziyuanshangmeiyou-access-control-allow-originbiaotou.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!