Tại sao tôi nhận được yêu cầu TÙY CHỌN thay vì yêu cầu NHẬN?


288
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script>
$.get("http://example.com/", function(data) {
     alert(data);
});
</script>

nó thực hiện một yêu cầu TÙY CHỌN cho URL đó và sau đó gọi lại không bao giờ được gọi với bất cứ điều gì.

Khi nó không phải là tên miền chéo, nó hoạt động tốt.

Không phải jQuery chỉ thực hiện cuộc gọi bằng một <script>nút và sau đó thực hiện cuộc gọi lại khi được tải? Tôi hiểu rằng tôi sẽ không thể nhận được kết quả (vì đó là tên miền chéo), nhưng điều đó ổn; Tôi chỉ muốn cuộc gọi đi qua. Đây có phải là một lỗi, hoặc tôi đang làm gì đó sai?


2
Có thể là cos của tên miền chéo. Ví dụ: nếu bạn đang ở trong tệp của mình Tệp: // PATH_TO_weBSITE thay vì sử dụng localhost / WEBSITE_LINK
James111

Câu trả lời:


262

Theo MDN ,

Yêu cầu được chiếu trước

Không giống như các yêu cầu đơn giản (đã thảo luận ở trên), trước tiên, các yêu cầu "được chiếu trước" gửi tiêu đề yêu cầu HTTP TÙY CHỌN đến tài nguyên trên miền khác, để xác định xem yêu cầu thực tế có an toàn để gửi hay không. Các yêu cầu trên nhiều trang web được chiếu trước như thế này vì chúng có thể có liên quan đến dữ liệu người dùng. Cụ thể, một yêu cầu được nêu trước nếu:

  • Nó sử dụng các phương thức khác ngoài GET hoặc POST. Ngoài ra, nếu POST được sử dụng để gửi dữ liệu yêu cầu với Loại nội dung không phải là application / x-www-form-urlencoding, Multipart / form-data hoặc text / plain, ví dụ: nếu yêu cầu POST gửi tải trọng XML đến máy chủ sử dụng application / xml hoặc text / xml, sau đó yêu cầu được chiếu trước.
  • Nó đặt các tiêu đề tùy chỉnh trong yêu cầu (ví dụ: yêu cầu sử dụng một tiêu đề như X-PINGOTHER)

43
điều này đã khắc phục sự cố của chúng tôi, việc thay đổi từ "application / json" thành "text / plain" đã dừng yêu cầu tùy chọn khủng khiếp
Keeno

10
Điều tôi không hiểu là tại sao trình duyệt lại yêu cầu với phương thức TÙY CHỌN chỉ để kiểm tra yêu cầu thực tế có an toàn để gửi không. nhưng theo nghĩa nào? ý tôi là máy chủ cũng có thể đặt các hạn chế với các tiêu đề phản hồi nhất định vậy tại sao điều này lại cần thiết?
hardik

11
@hardik Hãy nhớ rằng bằng cách thêm CORS, bạn có khả năng chấp nhận yêu cầu từ bất kỳ ai, trong đó họ có thể thao tác dữ liệu trên máy chủ của bạn thông qua các yêu cầu (POST, PUT, DELETE, v.v.). Trong những tình huống này, như khi sử dụng các tiêu đề tùy chỉnh, trình duyệt chỉ kiểm tra với máy chủ trước rằng máy chủ sẵn sàng chấp nhận yêu cầu trước khi gửi nó vì việc gửi yêu cầu không được yêu cầu đến máy chủ có thể thực sự nguy hiểm cho dữ liệu của bạn và cả những gì điểm trong trình duyệt gửi tải trọng lớn tiềm năng nếu máy chủ không muốn chấp nhận chúng, do đó kiểm tra TÙY CHỌN trước chuyến bay.
davidnknight

6
@davidnknight nếu việc gửi dữ liệu của bạn đến máy chủ có thể nguy hiểm, có nghĩa là máy chủ có thể bị xâm phạm, thì dĩ nhiên máy chủ độc hại sẽ phản hồi yêu cầu TÙY CHỌN của bạn với "Chắc chắn, gửi đi tất cả!". Làm thế nào là an ninh? (câu hỏi trung thực)
Matt

3
"yêu cầu preflight không phải là một điều bảo mật. Thay vào đó, chúng là một thứ không thay đổi theo quy tắc." - Xem câu trả lời Động lực đằng sau việc giới thiệu các yêu cầu chiếu trước là gì
FMJaguar


9

Nếu bạn đang cố gắng POST

Hãy chắc chắn với JSON.stringifydữ liệu biểu mẫu của bạn và gửi dưới dạng text/plain.

<form id="my-form" onSubmit="return postMyFormData();">
    <input type="text" name="name" placeholder="Your Name" required>
    <input type="email" name="email" placeholder="Your Email" required>
    <input type="submit" value="Submit My Form">
</form>

function postMyFormData() {

    var formData = $('#my-form').serializeArray();
    formData = formData.reduce(function(obj, item) {
        obj[item.name] = item.value;
        return obj;
    }, {});
    formData = JSON.stringify(formData);

    $.ajax({
        type: "POST",
        url: "https://website.com/path",
        data: formData,
        success: function() { ... },
        dataType: "text",
        contentType : "text/plain"
    });
}

2

Tôi không tin rằng jQuery sẽ tự nhiên thực hiện một yêu cầu JSONP khi được cung cấp một URL như thế. Tuy nhiên, nó sẽ thực hiện một yêu cầu JSONP khi bạn cho nó biết nên sử dụng đối số nào cho một cuộc gọi lại:

$.get("http://metaward.com/import/http://metaward.com/u/ptarjan?jsoncallback=?", function(data) {
     alert(data);
});

Hoàn toàn tùy thuộc vào tập lệnh nhận để sử dụng đối số đó (mà không phải gọi là "jsoncallback"), vì vậy trong trường hợp này, hàm sẽ không bao giờ được gọi. Nhưng, vì bạn đã nói rằng bạn chỉ muốn tập lệnh tại metaward.com thực thi, điều đó sẽ làm cho nó.


cuộc gọi lại của tôi vẫn được thông báo rằng phần tử script đã được tải đầy đủ? Tôi chỉ muốn đảm bảo rằng bản cập nhật đã xảy ra trước khi tôi truy vấn API cho nó.
Paul Tarjan

Bạn sẽ nếu tập lệnh nhận hỗ trợ JSONP và sẵn sàng gọi hàm bạn xác định. Nếu tập lệnh không làm gì ngoài việc tạo một khối dữ liệu JSON mà không có hành vi nào khác, bạn sẽ không thể biết khi nào tải xong. Nếu cần thiết phải biết khi nào tải xong, bạn có thể xem xét triển khai tập lệnh trên máy chủ của mình hoạt động như một proxy.
VoteyDiscipl

1

Trên thực tế, các yêu cầu AJAX (XMLHttp) tên miền chéo không được phép vì lý do bảo mật (nghĩ về việc tìm nạp trang web "bị hạn chế" từ phía máy khách và gửi lại cho máy chủ - đây sẽ là một vấn đề bảo mật).

Cách giải quyết duy nhất là cuộc gọi lại. Đây là: tạo một đối tượng tập lệnh mới và trỏ src tới JavaScript phía cuối, đó là một cuộc gọi lại với các giá trị JSON (myFunction ({data}), myFunction là một hàm thực hiện một cái gì đó với dữ liệu (ví dụ: lưu trữ nó trong một biến).


1
đúng, nhưng tôi có thể tải nó trong <script src = ""> hoặc <img src = ""> và trình duyệt sẽ vui vẻ nhấn nó. Tôi chỉ muốn biết khi nào nó được tải đầy đủ để tôi có thể truy vấn kết quả của việc nhập.
Paul Tarjan

1

Chỉ cần thay đổi "application / json" thành "text / plain" và đừng quên JSON.opesify (request):

var request = {Company: sapws.dbName, UserName: username, Password: userpass};
    console.log(request);
    $.ajax({
        type: "POST",
        url: this.wsUrl + "/Login",
        contentType: "text/plain",
        data: JSON.stringify(request),

        crossDomain: true,
    });

1

Tôi đã từng gặp vấn đề tương tự. Cách khắc phục của tôi là thêm các tiêu đề vào tập lệnh PHP của tôi chỉ xuất hiện khi ở trong môi trường dev.

Điều này cho phép các yêu cầu tên miền chéo:

header("Access-Control-Allow-Origin: *");

Điều này cho biết yêu cầu preflight rằng khách hàng có thể gửi bất kỳ tiêu đề nào họ muốn:

header("Access-Control-Allow-Headers: *");

Cách này không cần phải sửa đổi yêu cầu.

Nếu bạn có dữ liệu nhạy cảm trong cơ sở dữ liệu dev của bạn có khả năng bị rò rỉ, thì bạn có thể nghĩ hai lần về điều này.


1

Trong trường hợp của tôi, vấn đề không liên quan đến CORS kể từ khi tôi phát hành jQuery POST cho cùng một máy chủ web. Dữ liệu là JSON nhưng tôi đã bỏ qua tham số dataType: 'json'.

Tôi không có (tôi cũng không thêm) một tham số contentType như trong câu trả lời của David Lopes ở trên.


0

Nó trông giống như Firefox và Opera (cũng đã được thử nghiệm trên mac) không giống như tên miền chéo của điều này (nhưng Safari vẫn ổn với nó).

Bạn có thể phải gọi mã phía máy chủ cục bộ để cuộn trang từ xa.


0

Tôi đã có thể sửa nó với sự giúp đỡ của các tiêu đề sau

Access-Control-Allow-Origin
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Allow-Methods

Nếu bạn đang ở trên Nodejs, đây là mã bạn có thể sao chép / dán.

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH');
  next();
});
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.