Tại sao Postman không nhận được tiêu đề của No No 'Control-Control-allow-Origin' trên lỗi tài nguyên được yêu cầu khi mã JavaScript của tôi xảy ra?


2503

Mod lưu ý : Câu hỏi này là về lý do tại sao Postman không bị hạn chế CORS theo cách tương tự như XMLHttpRequest. Câu hỏi này không phải là về cách khắc phục lỗi "Không 'Kiểm soát truy cập-Cho phép-Xuất xứ' ...".

Hãy ngừng đăng :


Tôi cố gắng để làm phép sử dụng JavaScript bằng cách kết nối với RESTful API tích hợp Flask . Tuy nhiên, khi tôi thực hiện yêu cầu, tôi gặp lỗi sau:

XMLHttpRequest không thể tải http: // myApiUrl / đăng nhập . Không có tiêu đề 'Kiểm soát truy cập-Cho phép-Xuất xứ' trên tài nguyên được yêu cầu. Do đó, nguồn gốc 'null' không được phép truy cập.

Tôi biết rằng API hoặc tài nguyên từ xa phải đặt tiêu đề, nhưng tại sao nó hoạt động khi tôi làm theo yêu cầu thông qua phần mở rộng Chrome Postman ?

Đây là mã yêu cầu:

$.ajax({
    type: "POST",
    dataType: 'text',
    url: api,
    username: 'user',
    password: 'pass',
    crossDomain : true,
    xhrFields: {
        withCredentials: true
    }
})
    .done(function( data ) {
        console.log("done");
    })
    .fail( function(xhr, textStatus, errorThrown) {
        alert(xhr.responseText);
        alert(textStatus);
    });

32
Bạn đang thực hiện yêu cầu từ localhost hoặc direcly thực thi HTML?
MD. Sahib Bin Mahboob

@ MD.SahibBinMahboob Nếu tôi hiểu câu hỏi của bạn, tôi yêu cầu từ localhost - Tôi có trang trên máy tính của mình và chỉ cần chạy nó. Khi tôi triển khai trang web trên hosting, nó cũng cho kết quả tương tự.
Ông Jedi


8
Đối với bất kỳ ai đang tìm đọc thêm, MDN có một bài viết hay về ajax và các yêu cầu nguồn gốc chéo: developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Sam Eaton

1
Một lưu ý quan trọng cho loại lỗi này trong nút js. Bạn PHẢI đặt các tiêu đề truy cập của mình khi khởi động tệp server.js TRƯỚC KHI bắt đầu thiết lập các tuyến đường của bạn. Nếu không, mọi thứ sẽ chạy rất tốt, nhưng bạn sẽ gặp lỗi này khi bạn thực hiện các yêu cầu từ ứng dụng của mình.
Alex J

Câu trả lời:


1346

Nếu tôi hiểu đúng, bạn đang thực hiện một XMLHttpRequest cho một tên miền khác với trang của bạn. Vì vậy, trình duyệt đang chặn nó vì nó thường cho phép một yêu cầu có cùng nguồn gốc vì lý do bảo mật. Bạn cần phải làm một cái gì đó khác nhau khi bạn muốn thực hiện một yêu cầu tên miền chéo. Hướng dẫn về cách đạt được điều đó là Sử dụng CORS .

Khi bạn đang sử dụng người đưa thư, họ không bị hạn chế bởi chính sách này. Được trích dẫn từ XMLHttpRequest :

Các trang web thông thường có thể sử dụng đối tượng XMLHttpRequest để gửi và nhận dữ liệu từ các máy chủ từ xa, nhưng chúng bị giới hạn bởi cùng một chính sách gốc. Phần mở rộng không quá giới hạn. Một tiện ích mở rộng có thể nói chuyện với các máy chủ từ xa bên ngoài nguồn gốc của nó, miễn là lần đầu tiên nó yêu cầu quyền truy cập chéo.


7
Bạn đúng. Tôi yêu cầu tên miền khác với trang của tôi. API là trên máy chủ và tôi chạy yêu cầu từ localhost. Trước khi tôi chấp nhận câu trả lời, bạn có thể giải thích cho tôi nghĩa là "thực hiện yêu cầu trực tiếp" không? POSTman không sử dụng tên miền?
Ông Jedi

181
Trình duyệt không chặn yêu cầu. Các trình duyệt duy nhất chặn hoàn toàn các yêu cầu ajax có nguồn gốc chéo là IE7 trở lên. Tất cả các trình duyệt, trừ IE7 trở lên, đều triển khai thông số CORS (IE8 & IE9 một phần). Tất cả những gì bạn cần làm là chọn tham gia vào các yêu cầu CORS trên máy chủ API của bạn bằng cách trả lại các tiêu đề phù hợp dựa trên yêu cầu. Bạn nên đọc các khái niệm về CORS tại mzl.la/VOFrSz . Người đưa thư cũng gửi yêu cầu qua XHR. Nếu bạn không thấy vấn đề tương tự khi sử dụng người đưa thư, điều này có nghĩa là bạn vô tình không gửi cùng một yêu cầu qua người đưa thư.
Ray Nicholus

10
@ MD.SahibBinMahboob Người đưa thư KHÔNG gửi yêu cầu "từ mã java / python" của bạn. Nó đang gửi yêu cầu trực tiếp từ trình duyệt. XHR trong các tiện ích mở rộng của Chrome hoạt động hơi khác một chút, đặc biệt là khi các yêu cầu nguồn gốc chéo có liên quan .
Ray Nicholus

250

CẢNH BÁO: Việc sử dụng Access-Control-Allow-Origin: *có thể làm cho API / trang web của bạn dễ bị tấn công giả mạo yêu cầu chéo trang (CSRF). Hãy chắc chắn rằng bạn hiểu những rủi ro trước khi sử dụng mã này.

Rất đơn giản để giải quyết nếu bạn đang sử dụng PHP . Chỉ cần thêm đoạn mã sau vào đầu trang PHP của bạn để xử lý yêu cầu:

<?php header('Access-Control-Allow-Origin: *'); ?>

Nếu bạn đang sử dụng Node-red, bạn phải cho phép CORS trong node-red/settings.jstệp bằng cách bỏ bình luận các dòng sau:

// The following property can be used to configure cross-origin resource sharing
// in the HTTP nodes.
// See https://github.com/troygoode/node-cors#configuration-options for
// details on its contents. The following is a basic permissive set of options:
httpNodeCors: {
 origin: "*",
 methods: "GET,PUT,POST,DELETE"
},

Nếu bạn đang sử dụng Flask giống như câu hỏi; bạn có đầu tiên để cài đặtflask-cors

$ pip install -U flask-cors

Sau đó, bao gồm các bình Flask trong ứng dụng của bạn.

from flask_cors import CORS

Một ứng dụng đơn giản sẽ trông như:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"

Để biết thêm chi tiết, bạn có thể kiểm tra tài liệu Flask .


93
và nó không an toàn
llazzaro

153
Bạn không nên tắt CORS vì bạn không biết nó dùng để làm gì. Đây là một câu trả lời khủng khiếp.
meagar

124
Mặc dù nó có thể không an toàn, câu hỏi không phải là về bảo mật, mà là làm thế nào để hoàn thành nhiệm vụ. Đây là một trong những tùy chọn mà nhà phát triển phải lựa chọn khi xử lý các yêu cầu AJAX tên miền chéo. Nó giúp tôi giải quyết vấn đề và đối với ứng dụng của tôi, tôi không quan tâm dữ liệu đến từ đâu. Tôi vệ sinh tất cả các đầu vào với PHP trên miền đích, vì vậy, nếu ai đó muốn đăng một số rác lên nó, hãy để họ thử. Điểm chính ở đây là, AJAX tên miền chéo có thể được cho phép từ miền đích. +1 cho câu trả lời.
ZurabWeb

23
Mặc dù tôi đồng ý với thông điệp chung mà Piero đang đưa ra, đó không phải là cụ thể về bảo mật, nhưng bảo mật là một mối quan tâm. Tôi nghĩ điều này ít nhất nên nói điều gì đó như "Điều này nói chung là tồi tệ! Đừng làm điều này trừ khi bạn biết bạn đang làm gì! Đây là tài liệu thêm về nó: ...", và có thể giải thích ngắn gọn lý do tại sao. Tôi ghét ai đó đến đây và chỉ nghĩ "Ồ, tôi chỉ có thể thêm / điều chỉnh tiêu đề này và tôi ổn!" và không biết sự phân nhánh đầy đủ. Ý tôi là, đó là loại để họ nghiên cứu và tất cả, nhưng vẫn còn.
Thomas F.

4
Tôi thích câu trả lời này ... Tôi có cùng một vấn đề và nó đã giải quyết nó ... Ông giải thích có một số vấn đề bảo mật, nhưng đó là một vấn đề khác và để mọi người cá nhân suy nghĩ và giải quyết vấn đề đó ...
Ari Waisberg

64

Bởi vì
$ .ajax ({type: "POST" - gọi TÙY CHỌN
$ .post ( - Gọi POST

Cả hai đều khác nhau. Người đưa thư gọi "POST" đúng cách, nhưng khi chúng tôi gọi nó, nó sẽ là "TÙY CHỌN".

Đối với dịch vụ web C # - API Web

Vui lòng thêm mã sau vào tệp web.config trong thẻ <system.webServer>. Điều này sẽ làm việc:

<httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
    </customHeaders>
</httpProtocol>

Vui lòng đảm bảo rằng bạn không thực hiện bất kỳ lỗi nào trong cuộc gọi Ajax

jQuery

$.ajax({
    url: 'http://mysite.microsoft.sample.xyz.com/api/mycall',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    type: "POST", /* or type:"GET" or type:"PUT" */
    dataType: "json",
    data: {
    },
    success: function (result) {
        console.log(result);
    },
    error: function () {
        console.log("error");
    }
});

Lưu ý: Nếu bạn đang tìm kiếm tải xuống nội dung từ trang web của bên thứ ba thì điều này sẽ không giúp bạn . Bạn có thể thử đoạn mã sau, nhưng không phải JavaScript.

System.Net.WebClient wc = new System.Net.WebClient();
string str = wc.DownloadString("http://mysite.microsoft.sample.xyz.com/api/mycall");

Cấu hình này đã giải quyết lỗi tương tự trên Wordpress tại Azure Services. Cảm ơn.
Andre Mesquita

9
Tôi sẽ đề nghị sử dụng một giá trị nguồn gốc cụ thể để tránh các yêu cầu từ các miền bên ngoài. Vì vậy, ví dụ thay vì *sử dụnghttps://www.myotherdomain.com
pechar


8

Áp dụng hạn chế CORS là một tính năng bảo mật được xác định bởi máy chủ và được trình duyệt triển khai .

Trình duyệt xem chính sách CORS của máy chủ và tôn trọng nó.

Tuy nhiên, công cụ Postman không bận tâm về chính sách CORS của máy chủ.

Đó là lý do tại sao lỗi CORS xuất hiện trong trình duyệt, nhưng không xuất hiện trong Postman.


1
Vâng, tôi không thể nhấn mạnh đủ tại sao chi tiết nhỏ này đáng được chú ý. Khi nói về bảo mật, điều tối quan trọng cần đề cập là CORS chỉ mạnh như khách hàng thực hiện nó. Vì vậy, hãy tưởng tượng bạn lấy một httpClient đơn giản (mã phía máy chủ) và xây dựng một proxy, người sau đó thực hiện các yêu cầu của bạn ... Bảo mật có thể bị phá vỡ hoàn toàn, thực sự rời khỏi tiêu chuẩn CORS như một giải pháp kém
Christopher Bonitz

7

Trong cuộc điều tra dưới đây dưới dạng API, tôi sử dụng http://example.com thay vì http: // myApiUrl / đăng nhập từ câu hỏi của bạn, vì điều này đầu tiên hoạt động.

Tôi giả sử rằng trang của bạn trên http: //my-site.local: 8088 .

Lý do tại sao bạn thấy kết quả khác nhau là Postman:

  • đặt tiêu đề Host=example.com(API của bạn)
  • KHÔNG đặt tiêu đề Origin

Điều này tương tự như cách gửi yêu cầu của trình duyệt khi trang web và API có cùng tên miền (trình duyệt cũng đặt mục tiêu đề Referer=http://my-site.local:8088, tuy nhiên tôi không thấy điều đó trong Postman). Khi Origintiêu đề không được đặt, thông thường các máy chủ cho phép các yêu cầu như vậy theo mặc định.

Nhập mô tả hình ảnh ở đây

Đây là cách tiêu chuẩn để Postman gửi yêu cầu. Nhưng một trình duyệt gửi các yêu cầu khác nhau khi trang web và API của bạn có các tên miền khác nhau và sau đó CORS xảy ra và trình duyệt tự động:

  • đặt tiêu đề Host=example.com(của bạn là API)
  • đặt tiêu đề Origin=http://my-site.local:8088(trang web của bạn)

(Tiêu đề Referercó cùng giá trị như Origin). Và bây giờ trong tab Bảng điều khiển & Mạng của Chrome, bạn sẽ thấy:

Nhập mô tả hình ảnh ở đây

Nhập mô tả hình ảnh ở đây

Khi bạn có Host != OriginCORS và khi máy chủ phát hiện yêu cầu như vậy, nó thường chặn nó theo mặc định .

Origin=nullđược đặt khi bạn mở nội dung HTML từ thư mục cục bộ và nó sẽ gửi yêu cầu. Tình huống tương tự là khi bạn gửi yêu cầu bên trong <iframe>, như trong đoạn trích dưới đây (nhưng ở đây Hosttiêu đề hoàn toàn không được đặt) - nói chung, ở mọi nơi, đặc tả HTML đều nói nguồn gốc mờ, bạn có thể dịch nó sang Origin=null. Thông tin thêm về điều này bạn có thể tìm thấy ở đây .

fetch('http://example.com/api', {method: 'POST'});
Look on chrome-console > network tab

Nếu bạn không sử dụng một yêu cầu CORS đơn giản, thông thường trình duyệt cũng tự động gửi yêu cầu TÙY CHỌN trước khi gửi yêu cầu chính - có thêm thông tin ở đây . Đoạn mã dưới đây cho thấy nó:

fetch('http://example.com/api', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json'}
});
Look in chrome-console -> network tab to 'api' request.
This is the OPTIONS request (the server does not allow sending a POST request)

Bạn có thể thay đổi cấu hình máy chủ của mình để cho phép các yêu cầu CORS.

Dưới đây là một cấu hình ví dụ bật CORS trên nginx (tệp nginx.conf) - hãy cẩn thận với cài đặt always/"$http_origin"cho nginx và "*"cho Apache - điều này sẽ bỏ chặn CORS khỏi bất kỳ miền nào.

Đây là một cấu hình ví dụ bật CORS trên Apache (tệp .htaccess)


2

Gặp lỗi tương tự trong trường hợp sử dụng khác nhau.

Ca sử dụng: Trong chrome khi cố gắng gọi điểm cuối Spring REST theo góc.

nhập mô tả hình ảnh ở đây

Giải pháp: Thêm chú thích @CrossOrigin ("*") trên đầu Lớp điều khiển tương ứng.

nhập mô tả hình ảnh ở đây


Tôi sử dụng localhost thay vì * để bảo mật
neo7bf

-1

Nếu bạn sử dụng .NET làm tầng giữa, hãy kiểm tra rõ ràng thuộc tính tuyến đường, ví dụ:

Tôi đã có vấn đề khi nó như thế này,

[Route("something/{somethingLong: long}")] //Space.

Đã sửa nó bằng cách này,

[Route("something/{somethingLong:long}")] //No space

-1

Chỉ dành cho dự án API .NET Web, thêm các thay đổi sau:

  1. Thêm mã sau services.AddMvc()dòng sau trong ConfigureServices()phương thức của tệp Startup.cs:
services.AddCors(allowsites=>{allowsites.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
            });
  1. Thêm mã sau app.UseMvc()dòng sau trong Configure()phương thức của tệp Startup.cs:
app.UseCors(options => options.AllowAnyOrigin());
  1. Mở bộ điều khiển mà bạn muốn truy cập bên ngoài miền và thêm thuộc tính sau ở cấp điều khiển:
[EnableCors("AllowOrigin")]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.