jQuery $ .ajax (), $ .post gửi TÙY CHỌN LỰA CHỌN Ở dạng REQUEST_METHOD trong Firefox


330

Gặp sự cố với những gì tôi nghĩ là một plugin jQuery tương đối đơn giản ...

Plugin nên tìm nạp dữ liệu từ tập lệnh php thông qua ajax để thêm tùy chọn vào a <select>. Yêu cầu ajax khá chung chung:

$.ajax({
  url: o.url,
  type: 'post',
  contentType: "application/x-www-form-urlencoded",
  data: '{"method":"getStates", "program":"EXPLORE"}',
  success: function (data, status) {
    console.log("Success!!");
    console.log(data);
    console.log(status);
  },
  error: function (xhr, desc, err) {
    console.log(xhr);
    console.log("Desc: " + desc + "\nErr:" + err);
  }
});

Điều này dường như hoạt động tốt trong Safari. Trong Firefox 3.5, REQUEST_TYPEtrên máy chủ luôn là 'TÙY CHỌN' và dữ liệu $ _POST không xuất hiện. Apache ghi lại yêu cầu dưới dạng 'TÙY CHỌN':

::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46

Tại sao cuộc gọi ajax này hoạt động trong Safari, nhưng không phải Firefox và làm cách nào để khắc phục nó cho Firefox?

Tiêu đề phản hồi
Ngày: Thứ Tư, ngày 08 tháng 7 năm 2009 21:22:17 GMT
Máy chủ: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Độ dài nội dung 46
Hết thời gian chờ = 15, tối đa = 100
Giữ kết nối
Loại văn bản / html

Yêu cầu tiêu đề
Đơn đặt hàng máy chủ: 8888
Tác nhân người dùng Mozilla / 5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv: 1.9.1) Tắc kè / 20090624 Firefox / 3.5
Chấp nhận văn bản / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8
Ngôn ngữ chấp nhận en-us, en; q = 0,5
Chấp nhận mã hóa gzip, giảm phát
Chấp nhận-Bộ ký tự ISO-8859-1, utf-8; q = 0.7, *; q = 0.7
Giữ lại 300
Kết nối duy trì
Nguồn gốc http://ux.inetu.act.org
POST-Control-Request-Phương thức POST
Kiểm soát truy cập-Yêu cầu-Tiêu đề x-request-with

Dưới đây là hình ảnh của đầu ra Firebird:


Bạn có thể gửi phản hồi và yêu cầu tiêu đề. Tôi không nhận được bất kỳ lỗi nào khi tôi chạy mã tương tự trong Firefox.
MitMaro

Đã thêm thông tin tiêu đề và hình ảnh từ Fireorms.
fitzgeraldsteele

Chỉ có vấn đề tương tự trong khi thực hiện một máy chủ web nhúng. Cảm ơn bạn đã hỏi :)
Robert Gould

Nếu bạn đang tìm kiếm một giải pháp JAX-RS của Java, hãy xem tại đây: Kiểm soát truy cập-Cho phép-Xuất xứ
Tobias Sarnow

Hành vi của firefox dường như đã thay đổi bây giờ? Tôi không nhận được bất kỳ yêu cầu tùy chọn.
Buge

Câu trả lời:


169

Lý do cho lỗi là chính sách xuất xứ tương tự. Nó chỉ cho phép bạn thực hiện XMLHTTPRequests cho miền của riêng bạn. Xem nếu bạn có thể sử dụng một cuộc gọi lại JSONP thay thế:

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );

26
Tại sao firefox là trình duyệt duy nhất để làm điều này? Tôi muốn một bài viết không nhận được.
Maslow

11
Crossite-POST: Có ai biết giải pháp để thực hiện POST với ứng dụng / json là Kiểu nội dung không?
schoetbi

13
Vậy giải pháp chính xác là gì?
Nik So

3
Tìm kiếm một giải pháp cho vấn đề này và sử dụng getJSON thay vì cuộc gọi ajax không làm điều đó cho tôi vì nó hạn chế hơn nhiều.
Timo Wallenius

1
@schoetbi cho rằng bạn sẽ cần sử dụng CORS, được hỗ trợ tốt trong các trình duyệt mới hơn ... hỗ trợ hạn chế trong IE8-9 và cần hỗ trợ phía máy chủ.
Tracker1

57

Tôi đã sử dụng đoạn mã sau ở phía Django để diễn giải yêu cầu TÙY CHỌN và để đặt các tiêu đề Kiểm soát truy cập được yêu cầu. Sau đó, các yêu cầu tên miền chéo của tôi từ Firefox bắt đầu hoạt động. Như đã nói trước đó, trước tiên trình duyệt sẽ gửi yêu cầu TÙY CHỌN và sau đó ngay sau đó là POST / GET

def send_data(request):
    if request.method == "OPTIONS": 
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response['Access-Control-Max-Age'] = 1000
        # note that '*' is not valid for Access-Control-Allow-Headers
        response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
        return response
    if request.method == "POST":
        # ... 

Chỉnh sửa: có vẻ như ít nhất trong một số trường hợp, bạn cũng cần thêm các tiêu đề Kiểm soát truy cập tương tự vào phản hồi thực tế. Điều này có thể hơi khó hiểu, vì yêu cầu có vẻ thành công, nhưng Firefox không chuyển nội dung phản hồi cho Javascript.


Chỉnh sửa của bạn về phản hồi POST / GET thực tế là một chút đáng sợ; Nếu bất cứ ai có thể xác nhận điều đó, xin vui lòng cho chúng tôi biết ở đây!
Arjan

Tôi không biết đó là lỗi hay là một tính năng, nhưng dường như ai đó cũng đã chú ý đến nó. Xem ví dụ kodemaniak.de/?p=62 và tìm kiếm "cơ thể phản hồi trống rỗng"
Juha Palomäki

2
Có một sự khác biệt giữa các yêu cầu đơn giản và những yêu cầu cần ánh sáng trước. "Giải pháp" của bạn sẽ chỉ hoạt động với các yêu cầu preflight, vì vậy đó không phải là giải pháp thực sự. Bất cứ khi nào bạn nhận được "Xuất xứ:" - tiêu đề trong tiêu đề yêu cầu, bạn nên trả lời với điều đó được cho phép.
odinho - Velmont

1
Tôi tin rằng tiêu đề Access-Control-Allow-Headersnên chứa giá trị x-csrf-token, không x-csrftoken.
JellicleCat

16

Đây mozilla viết trên trung tâm phát triển mô tả kịch bản yêu cầu cross-domain khác nhau. Bài viết dường như chỉ ra rằng một yêu cầu POST với loại nội dung 'application / x-www-form-urlencoding' nên được gửi dưới dạng 'yêu cầu đơn giản' (không có yêu cầu 'TÙY CHỌN'). Tuy nhiên, tôi thấy rằng Firefox đã gửi yêu cầu TÙY CHỌN, mặc dù POST của tôi đã được gửi với loại nội dung đó.

Tôi đã có thể thực hiện công việc này bằng cách tạo trình xử lý yêu cầu tùy chọn trên máy chủ, đặt tiêu đề phản hồi 'Access-Control-Allow-Origin' thành '*'. Bạn có thể hạn chế hơn bằng cách đặt nó thành một cái gì đó cụ thể, như ' http://someurl.com '. Ngoài ra, tôi đã đọc rằng, được cho là, bạn có thể chỉ định một danh sách nhiều nguồn gốc được phân tách bằng dấu phẩy, nhưng tôi không thể làm việc này.

Khi Firefox nhận được phản hồi cho yêu cầu TÙY CHỌN với giá trị 'Kiểm soát truy cập-Cho phép-Xuất xứ' được chấp nhận, nó sẽ gửi yêu cầu POST.


15

Tôi đã khắc phục sự cố này bằng giải pháp hoàn toàn dựa trên Apache. Trong vhost / htaccess của tôi, tôi đặt khối sau:

# enable cross domain access control
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"

# force apache to return 200 without executing my scripts
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]

Bạn có thể không cần phần sau, tùy thuộc vào những gì xảy ra khi Apache thực thi tập lệnh đích của bạn. Tín dụng cho dân gian ServerFault thân thiện cho phần sau.


Câu trả lời của bạn đã giúp tôi, nhưng nếu cần một số logic đằng sau CORS, nó không giải quyết được hoàn toàn.
Ratata Tata

10

PHP này ở đầu tập lệnh trả lời dường như hoạt động. (Với Firefox 3.6.11. Tôi chưa thực hiện nhiều thử nghiệm.)

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) {
    header('Access-Control-Allow-Headers: '
           . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
} else {
    header('Access-Control-Allow-Headers: *');
}

if("OPTIONS" == $_SERVER['REQUEST_METHOD']) {
    exit(0);
}

Đây có thể là một vấn đề của hương vị, nhưng luôn gửi những tiêu đề phản ứng (cũng cho GET, POST, ...) là một chút quá nhiều theo ý thích của tôi. (Và, tôi tự hỏi liệu có luôn gửi những thông báo tuân thủ thông số kỹ thuật không?)
Arjan

3
gói nó trong if ($ _ SERVER ['HTTP_ORIGIN']). Nếu tiêu đề đó ở đó, đó là một yêu cầu CORS, nếu không, tốt, không cần phải gửi bất cứ điều gì.
odinho - Velmont

7

Tôi gặp vấn đề tương tự với việc gửi yêu cầu tới google maps và giải pháp khá đơn giản với jQuery 1.5 - để sử dụng dataType dataType: "jsonp"


12
Không tương thích với phương thức POST.
Pavel Vlasov

1
Nó hoạt động với phương thức GET nhưng đó là một giải pháp rất hạn chế. Ví dụ: bằng cách đó, bạn không thể gửi lại phản hồi bằng một tiêu đề cụ thể bao gồm mã thông báo.
svassr

6

Thủ phạm là yêu cầu chiếu trước bằng phương pháp TÙY CHỌN

Đối với các phương thức yêu cầu HTTP có thể gây ra tác dụng phụ đối với dữ liệu người dùng (cụ thể là đối với các phương thức HTTP khác với GET hoặc sử dụng POST với một số loại MIME nhất định), đặc tả bắt buộc các trình duyệt "đánh dấu" yêu cầu, mời các phương thức được hỗ trợ từ máy chủ có phương thức yêu cầu HTTP TÙY CHỌN và sau đó, sau khi "phê duyệt" từ máy chủ, gửi yêu cầu thực tế với phương thức yêu cầu HTTP thực tế.

Đặc tả web tham khảo: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Tôi đã giải quyết vấn đề bằng cách thêm các dòng sau trong Nginx conf.

    location / {
               if ($request_method = OPTIONS ) {
                   add_header Access-Control-Allow-Origin  "*";
                   add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS";
                   add_header Access-Control-Allow-Headers "Authorization";
                   add_header Access-Control-Allow-Credentials  "true";
                   add_header Content-Length 0;
                   add_header Content-Type text/plain;
                   return 200;
               }
    location ~ ^/(xxxx)$ {
                if ($request_method = OPTIONS) {
                    rewrite ^(.*)$ / last;
                }
    }

1
Câu trả lời này rất hữu ích cảm ơn. Thực tế là trình duyệt đang gửi yêu cầu preflight bằng phương thức TÙY CHỌN là không rõ ràng.
Normangorman

4

Tôi đã xem qua nguồn 1.3.2, khi sử dụng JSONP, yêu cầu được thực hiện bằng cách xây dựng phần tử SCRIPT một cách linh hoạt, vượt qua chính sách của cùng một trình duyệt. Đương nhiên, bạn không thể thực hiện yêu cầu POST bằng phần tử SCRIPT, trình duyệt sẽ tìm nạp kết quả bằng GET.

Khi bạn yêu cầu một cuộc gọi JSONP, phần tử SCRIPT không được tạo, bởi vì nó chỉ thực hiện điều này khi Loại cuộc gọi AJAX được đặt thành GET.

http://dev.jquery.com/ticket/4690


3

Chúng tôi đã có một vấn đề như thế này với ASP.Net. IIS của chúng tôi đã trả về Lỗi Máy chủ Nội bộ khi cố gắng thực thi jQuery $.postđể nhận một số nội dung html do PageHandlerFactory bị hạn chế chỉ phản hồi GET,HEAD,POST,DEBUGĐộng từ. Vì vậy, bạn có thể thay đổi hạn chế đó khi thêm động từ "TÙY CHỌN" vào danh sách hoặc chọn "Tất cả các động từ"

Bạn có thể sửa đổi điều đó trong Trình quản lý IIS, chọn trang web của bạn, sau đó chọn Trình xử lý ánh xạ, nhấp đúp vào tệp PageHandlerFactory cho các tệp * .apx khi bạn cần (Chúng tôi sử dụng nhóm ứng dụng tích hợp với khung 4.0). Nhấp vào Yêu cầu hạn chế, sau đó chuyển đến Tabbs Verbs và áp dụng sửa đổi của bạn.

Bây giờ $.postyêu cầu của chúng tôi đang làm việc như mong đợi :)


2

Kiểm tra xem actionURL của biểu mẫu của bạn có bao gồm wwwmột phần của tên miền hay không, trong khi trang gốc bạn đã mở được xem mà không có www.

Thường được thực hiện cho Url Canonical ..

Tôi đã vật lộn trong nhiều giờ trước khi tình cờ thấy bài viết này và tìm thấy gợi ý của Cross Domain.


2

Tôi dường như nếu o.url = 'index.php' và tập tin này tồn tại là ok và trả về một thông báo thành công trong bảng điều khiển. Nó trả về lỗi nếu tôi sử dụng url:http://www.google.com

Nếu thực hiện một yêu cầu bài đăng tại sao không sử dụng trực tiếp $ .post phương thức :

$.post("test.php", { func: "getNameAndTime" },
    function(data){
        alert(data.name); // John
        console.log(data.time); //  2pm
    }, "json");

Nó đơn giản hơn nhiều.


Có điều tương tự với điều này ... nghĩ rằng tôi nên sử dụng $ .ajax () để tôi ít nhất có thể nhận được một số thông tin gỡ lỗi về tình trạng lỗi ..
fitzgeraldsteele


1

Giải pháp cho vấn đề này là:

  1. sử dụng loại dữ liệu: json
  2. thêm &callback=?vào url của bạn

điều này đã làm việc khi gọi API Facebook và với Firefox. Fireorms đang sử dụng GETthay vì OPTIONSvới các điều kiện trên (cả hai).



0

Bạn có thể thử cái này mà không

contentType:application/x-www-form-urlencoded


Kết quả tương tự, tôi sợ.
fitzgeraldsteele

0

Hãy thử thêm tùy chọn:

Kiểu dữ liệu: "json"


2
đã làm việc, tại sao json được coi là "an toàn" cho các yêu cầu tên miền chéo?
Nik So

0
 function test_success(page,name,id,divname,str)
{ 
 var dropdownIndex = document.getElementById(name).selectedIndex;
 var dropdownValue = document.getElementById(name)[dropdownIndex].value;
 var params='&'+id+'='+dropdownValue+'&'+str;
 //makerequest_sp(url, params, divid1);

 $.ajax({
    url: page,
    type: "post",
    data: params,
    // callback handler that will be called on success
    success: function(response, textStatus, jqXHR){
        // log a message to the console
        document.getElementById(divname).innerHTML = response;

        var retname = 'n_district';
        var dropdownIndex = document.getElementById(retname).selectedIndex;
        var dropdownValue = document.getElementById(retname)[dropdownIndex].value;
        if(dropdownValue >0)
        {
            //alert(dropdownValue);
            document.getElementById('inputname').value = dropdownValue;
        }
        else
        {
            document.getElementById('inputname').value = "00";
        }
        return;
        url2=page2; 
        var params2 = parrams2+'&';
        makerequest_sp(url2, params2, divid2);

     }
});         
}

Câu hỏi đã được trả lời 6 tháng trước. Làm thế nào để giải quyết nó?
Barmar

0

Tôi gặp vấn đề tương tự khi thử sử dụng API Facebook.

ContentType duy nhất không gửi yêu cầu Preflighted dường như chỉ là văn bản / thuần túy ... không phải là phần còn lại của các tham số được đề cập tại mozilla tại đây

  • Tại sao đây là trình duyệt duy nhất làm điều này?
  • Tại sao Facebook không biết và chấp nhận yêu cầu preflight?

FYI: Tài liệu Moz đã nói ở trên cho thấy các tiêu đề X-Lori sẽ kích hoạt yêu cầu Preflighted ... không.


0

Bạn cần phải làm một số công việc về phía máy chủ. Tôi thấy bạn đang sử dụng PHP ở phía máy chủ, nhưng giải pháp cho ứng dụng web .NET có ở đây: Không thể đặt loại nội dung thành 'application / json' trong jQuery.ajax

Làm tương tự trong kịch bản PHP và nó sẽ hoạt động. Đơn giản: Tại trình duyệt yêu cầu đầu tiên là yêu cầu máy chủ nếu được phép gửi dữ liệu đó với loại đó và yêu cầu thứ hai là phù hợp / được phép.


0

Hãy thử thêm vào như sau:

dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),  

0

Tôi đã sử dụng một url proxy để giải quyết vấn đề tương tự khi tôi muốn đăng dữ liệu lên apache solr của mình được lưu trữ trong một máy chủ khác. (Đây có thể không phải là câu trả lời hoàn hảo nhưng nó giải quyết vấn đề của tôi.)

Theo URL này: Sử dụng Chế độ-Viết lại để ủy quyền , tôi thêm dòng này vào httpd.conf của mình:

 RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]

Do đó, tôi chỉ có thể đăng dữ liệu lên / solr thay vì đăng dữ liệu lên http: // ip: 8983 / solr / *. Sau đó, nó sẽ được đăng dữ liệu trong cùng một nguồn gốc.


0

Tôi đã có mã này xử lý tốt tình huống cors của tôi trong php:

header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Credentials: true' );

Và nó hoạt động tốt ở địa phương và từ xa, nhưng không phải để tải lên khi ở xa.

Một cái gì đó xảy ra với apache / php HOẶC mã của tôi, tôi không bận tâm tìm kiếm nó, khi bạn yêu cầu TÙY CHỌN, nó sẽ trả về tiêu đề của tôi với các quy tắc cors nhưng với kết quả 302. Do đó, trình duyệt của tôi không nhận ra đây là một tình huống có thể chấp nhận được.

Những gì tôi đã làm, dựa trên câu trả lời của @Mark McDonald, chỉ cần đặt mã này sau tiêu đề của tôi:

if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
    header("HTTP/1.1 202 Accepted");
    exit;
}

Bây giờ, khi yêu cầu OPTIONSnó sẽ chỉ gửi tiêu đề và kết quả 202.


-1

Xin lưu ý:

JSONP chỉ hỗ trợ phương thức yêu cầu GET.

* Gửi yêu cầu của firefox : *

$.ajax({
   type: 'POST',//<<===
   contentType: 'application/json',
   url: url,
   dataType: "json"//<<=============
    ...
});

Yêu cầu trên gửi bởi TÙY CHỌN (while ==> gõ: 'POST' ) !!!!

$.ajax({
    type: 'POST',//<<===
    contentType: 'application/json',
    url: url,
    dataType: "jsonp"//<<==============
    ...
});

Nhưng yêu cầu ở trên gửi bằng GET (while ==> gõ: 'POST' ) !!!!

Khi bạn đang ở trong "giao tiếp giữa các miền", hãy chú ý và cẩn thận.

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.