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

本文翻译自:No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API

I'm trying to fetch some data from the REST API of HP Alm. 我正在尝试从HP Alm的REST API中获取一些数据。 It works pretty well with a small curl script - I get my data. 它与一个小的curl脚本一起使用时效果很好-我得到了数据。

Now doing that with JavaScript, fetch and ES6 (more or less) seems to be a bigger issue. 现在使用JavaScript进行操作,获取和ES6(或多或少)似乎是一个更大的问题。 I keep getting this error message: 我不断收到此错误消息:

Fetch API cannot load . 提取API无法加载。 Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. 对预检请求的响应未通过访问控制检查:请求的资源上不存在“ Access-Control-Allow-Origin”标头。 Origin ' http://127.0.0.1:3000 ' is therefore not allowed access. 因此,不允许访问源' http://127.0.0.1:3000 '。 The response had HTTP status code 501. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. 响应的HTTP状态码为501。如果不透明响应满足您的需求,请将请求的模式设置为“ no-cors”,以在禁用CORS的情况下获取资源。

I understand that this is because I am trying to fetch that data from within my localhost and the solution should be using CORS. 我了解这是因为我正在尝试从本地主机中获取数据,并且解决方案应使用CORS。 Now I thought I actually did that, but somehow it either ignores what I write in the header or the problem is something else? 现在我以为我确实这样做了,但是以某种方式它要么忽略了我在标题中写的内容,要么是其他问题?

So, is there an implementation issue? 那么,是否存在实施问题? Am I doing it wrong? 我做错了吗? I can't check the server logs unfortunately. 我无法检查服务器日志。 I'm really a bit stuck here. 我真的有点卡在这里。

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));
}

I am using Chrome. 我正在使用Chrome。 I also tried using that Chrome CORS Plugin, but then I am getting another error message: 我也尝试使用该Chrome CORS插件,但是随后出现另一条错误消息:

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. 当请求的凭据模式为“ include”时,响应中“ Access-Control-Allow-Origin”标头的值不得为通配符“ *”。 Origin ' http://127.0.0.1:3000 ' is therefore not allowed access. 因此,不允许访问源' http://127.0.0.1:3000 '。 The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute. XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制。


#1楼

参考:https://stackoom.com/question/2y513/尝试从REST-API获取数据时-请求的资源上没有-Access-Control-Allow-Origin-标头


#2楼

This answer covers a lot of ground, so it's divided into three parts: 该答案涉及很多领域,因此分为三个部分:

  • How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems 如何使用CORS代理来解决“无访问控制-允许-来源标头”问题
  • How to avoid the CORS preflight 如何避免CORS飞行前
  • How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems 如何解决“ Access-Control-Allow-Origin标头一定不能为通配符”的问题

How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems 如何使用CORS代理来解决“无访问控制-允许-来源标头”问题

If you don't control the server your frontend JavaScript code is sending a request to, and the problem with the response from that server is just the lack of the necessary Access-Control-Allow-Origin header, you can still get things to work—by making the request through a CORS proxy. 如果您不控制服务器,您的前端JavaScript代码正在向其发送请求,并且该服务器的响应问题仅在于缺少必要的Access-Control-Allow-Origin标头,那么您仍然可以使事情正常进行-通过CORS代理发出请求。 To show how that works, first here's some code that doesn't use a CORS proxy: 为了展示它是如何工作的,首先这里是一些不使用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?")) 

The reason the catch block gets hit there is, the browser prevents that code from accessing the response which comes back from https://example.com . 导致catch块被击中的原因是,浏览器阻止该代码访问来自https://example.com的响应。 And the reason the browser does that is, the response lacks the Access-Control-Allow-Origin response header. 而浏览器这样做的原因是,该响应缺少Access-Control-Allow-Origin响应标头。

Now, here's exactly the same example but just with a CORS proxy added in: 现在,这是完全相同的示例,只是在其中添加了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?")) 

Note: If https://cors-anywhere.herokuapp.com is down or unavailable when you try it, then see below for how to deploy your own CORS Anywhere server at Heroku in just 2-3 minutes. 注意:如果在尝试https://cors-anywhere.herokuapp.com时关闭或不可用,请参见下文,了解如何在2-3分钟内在Heroku上部署自己的CORS Anywhere服务器。

The second code snippet above can access the response successfully because taking the request URL and changing it to https://cors-anywhere.herokuapp.com/https://example.com —by just prefixing it with the proxy URL—causes the request to get made through that proxy, which then: 上面的第二个代码段可以成功访问响应,因为采用请求URL并将其更改为https://cors-anywhere.herokuapp.com/https://example.com(仅在其前面加上代理URL)会导致请求通过该代理取得,然后:

  1. Forwards the request to https://example.com . 将请求转发到https://example.com
  2. Receives the response from https://example.com . https://example.com接收响应。
  3. Adds the Access-Control-Allow-Origin header to the response. Access-Control-Allow-Origin标头添加到响应中。
  4. Passes that response, with that added header, back to the requesting frontend code. 将带有添加的标头的响应传递回请求的前端代码。

The browser then allows the frontend code to access the response, because that response with the Access-Control-Allow-Origin response header is what the browser sees. 然后,浏览器允许前端代码访问响应,因为带有Access-Control-Allow-Origin响应标头的响应就是浏览器看到的内容。

You can easily run your own proxy using code from https://github.com/Rob--W/cors-anywhere/ . 您可以使用https://github.com/Rob--W/cors-anywhere/中的代码轻松运行自己的代理。
You can also easily deploy your own proxy to Heroku in literally just 2-3 minutes, with 5 commands: 您还可以使用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

After running those commands, you'll end up with your own CORS Anywhere server running at, eg, https://cryptic-headland-94862.herokuapp.com/ . 运行完这些命令后,您将最终在以下位置运行自己的CORS Anywhere服务器,例如https://cryptic-headland-94862.herokuapp.com/ So then rather than prefixing your request URL with https://cors-anywhere.herokuapp.com , prefix it instead with the URL for your own instance; 因此,不要在请求URL前面加上https://cors-anywhere.herokuapp.com ,而是在您自己的实例的URL前面加上前缀; eg, https://cryptic-headland-94862.herokuapp.com/https://example.com . 例如https://cryptic-headland-94862.herokuapp.com/https://example.com

So if when you go to try to use https://cors-anywhere.herokuapp.com, you find it's down (which it sometimes will be), then consider getting a Heroku account (if you don't already) and take 2 or 3 minutes to do the steps above to deploy your own CORS Anywhere server on Heroku. 因此,如果您尝试使用https://cors-anywhere.herokuapp.com时发现它已关闭 (有时会出现故障 ),那么可以考虑获取一个Heroku帐户(如果您尚未使用)并拿2或花费3分钟完成上述步骤,以在Heroku上部署您自己的CORS Anywhere服务器。

Regardless, whether you run your own or use https://cors-anywhere.herokuapp.com or other open proxy, this solution will work even if the request is one that triggers browsers to do a CORS preflight OPTIONS request—because in that case, the proxy also sends back the Access-Control-Allow-Headers and Access-Control-Allow-Methods headers needed to make the preflight successful. 无论您是运行自己的网站还是使用https://cors-anywhere.herokuapp.com或其他开放式代理,即使该请求是触发浏览器执行CORS预检OPTIONS请求的请求,该解决方案都将起作用-因为在这种情况下,代理还会发回使预检成功所需的Access-Control-Allow-HeadersAccess-Control-Allow-Methods头。


How to avoid the CORS preflight 如何避免CORS飞行前

The code in the question triggers a CORS preflight—since it sends an Authorization header. 问题中的代码会触发CORS预检-因为它发送了Authorization标头。

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

Even without that, the Content-Type: application/json header would also trigger the preflight. 即使没有这些Content-Type: application/jsonContent-Type: application/json标头也将触发预检。

What “preflight” means: before the browser tries the POST in the code in the question, it'll first send an OPTIONS request to the server — to determine if the server is opting-in to receiving a cross-origin POST that includes the Authorization and Content-Type: application/json headers. “预检”的含义是:在浏览器尝试问题代码中的POST之前,它将首先向服务器发送OPTIONS请求-确定服务器是否选择接收包含以下内容的跨域POSTAuthorizationContent-Type: application/json标头。

It works pretty well with a small curl script - I get my data. 它与一个小的curl脚本一起使用时效果很好-我得到了数据。

To properly test with curl , you need to emulate the preflight OPTIONS request the browser sends: 为了正确地使用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"

…with https://the.sign_in.url replaced by whatever your actual sign_in URL is. …将https://the.sign_in.url替换为您实际的sign_in URL。

The response the browser needs to see from that OPTIONS request must include headers like this: 浏览器需要从该OPTIONS请求中看到的响应必须包括以下标头:

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

If the OPTIONS response doesn't include those headers, then the browser will stop right there and never even attempt to send the POST request. 如果OPTIONS响应不包含这些标头,则浏览器将在那里停止,甚至从不尝试发送POST请求。 Also, the HTTP status code for the response must be a 2xx—typically 200 or 204. If it's any other status code, the browser will stop right there. 另外,响应的HTTP状态代码必须为2xx,通常为200或204。如果是其他任何状态代码,浏览器将在那里停止。

The server in the question is responding to the OPTIONS request with a 501 status code, which apparently means it's trying to indicate it doesn't implement support for OPTIONS requests. 问题中的服务器正在使用501状态代码来响应OPTIONS请求,这显然意味着它试图表明它未实现对OPTIONS请求的支持。 Other servers typically respond with a 405 “Method not allowed” status code in this case. 在这种情况下,其他服务器通常会以405“不允许使用方法”状态代码进行响应。

So you're never going to be able to make POST requests directly to that server from your frontend JavaScript code if the server responds to that OPTIONS request with a 405 or 501 or anything other than a 200 or 204 or if doesn't respond with those necessary response headers. 因此,如果服务器使用405或501或200或204以外的任何内容来响应该OPTIONS请求,或者如果没有响应,则您将永远无法从前端JavaScript代码直接向该服务器发出POST请求。那些必要的响应头。

The way to avoid triggering a preflight for the case in the question would be: 避免触发该问题的事前准备的方法是:

  • if the server didn't require an Authorization request header but instead (for example) relied on authentication data embedded in the body of the POST request or as a query parameter 如果服务器不需要Authorization请求标头,而是(例如)依赖于嵌入在POST请求正文中或作为查询参数的身份验证数据
  • if the server didn't require the POST body to have a Content-Type: application/json media type but instead accepted the POST body as application/x-www-form-urlencoded with a parameter named json (or whatever) whose value is the JSON data 如果服务器不要求POST正文具有Content-Type: application/json媒体类型,而是接受POST正文作为application/x-www-form-urlencoded且其参数名为json (或其他值),则为JSON数据

How to fix “Access-Control-Allow-Origin header must not be the wildcard” problems 如何解决“ Access-Control-Allow-Origin标头一定不能为通配符”的问题

I am getting another error message: 我收到另一个错误消息:

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. 当请求的凭据模式为“ include”时,响应中“ Access-Control-Allow-Origin”标头的值不得为通配符“ *”。 Origin ' http://127.0.0.1:3000 ' is therefore not allowed access. 因此,不允许访问源' http://127.0.0.1:3000 '。 The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute. XMLHttpRequest发起的请求的凭据模式由withCredentials属性控制。

For a request that includes credentials, browsers won't let your frontend JavaScript code access the response if the value of the Access-Control-Allow-Origin response header is * . 对于包含凭据的请求,如果Access-Control-Allow-Origin响应标头的值为* ,浏览器将不允许您的前端JavaScript代码访问响应。 Instead the value in that case must exactly match your frontend code's origin, http://127.0.0.1:3000 . 相反,在这种情况下,该值必须与前端代码的来源http://127.0.0.1:3000完全匹配。

See Credentialed requests and wildcards in the MDN HTTP access control (CORS) article. 请参阅MDN HTTP访问控制(CORS)文章中的凭据请求和通配符

If you control the server you're sending the request to, then a common way to deal with this case is to configure the server to take the value of the Origin request header, and echo/reflect that back into the value of the Access-Control-Allow-Origin response header. 如果您控制要向其发送请求的服务器,那么处理这种情况的一种常见方法是将服务器配置为采用Origin请求标头的值,并将其回显/反射回Access-Control-Allow-Origin的值。 Access-Control-Allow-Origin响应标头。 For example, with nginx: 例如,使用nginx:

add_header Access-Control-Allow-Origin $http_origin

But that's just one example; 但这只是一个例子。 other (web) server systems provide similar ways to echo origin values. 其他(网络)服务器系统提供了类似的方法来回显原始值。


I am using Chrome. 我正在使用Chrome。 I also tried using that Chrome CORS Plugin 我也尝试使用该Chrome CORS插件

That Chrome CORS plugin apparently just simplemindedly injects an Access-Control-Allow-Origin: * header into the response the browser sees. Chrome CORS插件显然只是简单地将Access-Control-Allow-Origin: *标头注入浏览器看到的响应中。 If the plugin were smarter, what it would be doing is setting the value of that fake Access-Control-Allow-Origin response header to the actual origin of your frontend JavaScript code, http://127.0.0.1:3000 . 如果插件更聪明,它将在将假的Access-Control-Allow-Origin响应标头的值设置为前端JavaScript代码http://127.0.0.1:3000的实际来源。

So avoid using that plugin, even for testing. 因此,即使进行测试,也请避免使用该插件。 It's just a distraction. 这只是分心。 If you want to test what responses you get from the server with no browser filtering them, you're better off using curl -H as above. 如果要测试从服务器获得的响应,而没有浏览器过滤响应,则最好使用上面的curl -H


As far as the frontend JavaScript code for the fetch(…) request in the question: 至于问题中的fetch(…)请求的前端JavaScript代码:

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

Remove those lines. 删除这些行。 The Access-Control-Allow-* headers are response headers. Access-Control-Allow-*标头是响应标头。 You never want to send them in a request. 您永远不想在请求中发送它们。 The only effect that'll have is to trigger a browser to do a preflight. 唯一的效果就是触发浏览器进行预检。


#3楼

This error occurs when the client URL and server URL don't match, including the port number. 当客户端URL和服务器URL不匹配(包括端口号)时,将发生此错误。 In this case you need to enable your service for CORS which is cross origin resource sharing. 在这种情况下,您需要为CORS启用服务,即跨源资源共享。

If you are hosting a Spring REST service then you can find it in the blog post CORS support in Spring Framework . 如果您要托管Spring REST服务,则可以在博客文章“ Spring Framework中的CORS支持”中找到它。

If you are hosting a service using a Node.js server then 如果您正在使用Node.js服务器托管服务,则

  1. Stop the Node.js server. 停止Node.js服务器。
  2. npm install cors --save
  3. Add following lines to your server.js 将以下行添加到您的server.js

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

#4楼

删除此:

credentials: 'include',

#5楼

Using dataType: 'jsonp' worked for me. 使用dataType: 'jsonp'对我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               

#6楼

I was working with Spring REST, and I solved it adding the AllowedMethods into the WebMvcConfigurer. 我正在使用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");
                }
            };
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章