嘗試從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");
                }
            };
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章