AJAX ใน Chrome ส่ง OPTIONS แทน GET / POST / PUT / DELETE?


107

ฉันกำลังทำงานกับเว็บแอปพลิเคชันภายในที่ทำงาน ใน IE10 คำขอใช้งานได้ดี แต่ใน Chrome คำขอ AJAX ทั้งหมด (ซึ่งมีจำนวนมาก) จะถูกส่งโดยใช้ OPTIONS แทนที่จะใช้วิธีการใดก็ตามที่ฉันให้ไว้ ในทางเทคนิคคำขอของฉันคือ "ข้ามโดเมน" ไซต์นี้ให้บริการบน localhost: 6120 และบริการที่ฉันกำลังส่งคำขอ AJAX อยู่บน 57124 ข้อผิดพลาด jquery แบบปิดนี้กำหนดปัญหา แต่ไม่ใช่การแก้ไขจริง

ฉันจะทำอย่างไรเพื่อใช้เมธอด http ที่เหมาะสมในคำขอ ajax

แก้ไข:

นี่คือการโหลดเอกสารของทุกหน้า:

jQuery.support.cors = true;

และ AJAX ทุกตัวถูกสร้างขึ้นในทำนองเดียวกัน:

var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
    url: url,
    dataType: "json",
    data: json,
    async: true,
    cache: false,
    timeout: 30000,
    headers: { "x-li-format": "json", "X-UserName": userName },
    success: function (data) {
        // my success stuff
    },
    error: function (request, status, error) {
        // my error stuff
    },
    type: "POST"
});

2
ความคิดเห็นสุดท้ายในรายงานข้อบกพร่องนั้นอธิบายได้ค่อนข้างดี ...
Kevin B

1
มันพลิกความคิดของฉันเพราะทุกสิ่งที่ฉันทำคือวานิลลา (และรหัสของฉันก็คล้ายกับในจุดบกพร่องของ jquery) นอกจากนี้ไม่มีข้อแก้ตัวใด ๆ ที่จะไม่รวมไว้ด้วย BRB คว้าโค้ดตัวอย่าง
Corey Ogburn

3
โปรดทราบว่า IE ไม่พิจารณาหมายเลขพอร์ตเมื่อพิจารณาว่าคำขอข้ามแหล่งกำเนิดหรือไม่
Ray Nicholus

@KevinB: บริการ REST ของเราใช้ประโยชน์จากคำขอที่แตกต่างกันในการทำสิ่งต่างๆตามวิธี http การเปลี่ยนทุกอย่างเป็น GET ไม่ใช่วิธีแก้ไขที่ถูกต้อง นอกจากนี้ตามคำตอบของ Dark Falcon มันก็ไม่ได้ช่วยอะไรเพราะฉันมี X-UserName และส่วนหัวที่กำหนดเองอื่น ๆ ในคำขอ
Corey Ogburn

ที่ไม่เปลี่ยนแปลงข้อเท็จจริงที่ว่าหากคุณต้องการส่งคำขอข้ามแหล่งที่มาคุณต้องปฏิบัติตามกฎทั้งหมดที่บังคับใช้กับคำขอข้ามแหล่งที่มาเพื่อให้คำขอนั้นทำงานได้อย่างถูกต้อง โดยทั่วไปแล้วคำขอข้ามแหล่งที่มาจะเกี่ยวข้องกับคำขอ OPTIONS จัดการให้ถูกต้องแล้วปัญหาจะหมดไป วิธีเดียวในการแก้ปัญหานี้ (โดยไม่ต้องเปลี่ยน api) คือการมีสคริปต์บนเซิร์ฟเวอร์เดียวกันกับเพจหลักที่โต้ตอบกับ api โดยใช้โค้ดฝั่งเซิร์ฟเวอร์
Kevin B

คำตอบ:


136

Chrome นำเสนอคำขอให้มองหาส่วนหัวCORSล่วงหน้า หากคำขอเป็นที่ยอมรับก็จะส่งคำขอจริง หากคุณกำลังดำเนินการข้ามโดเมนคุณจะต้องจัดการกับมันไม่เช่นนั้นจะหาวิธีทำให้คำขอไม่ข้ามโดเมน นี่คือสาเหตุที่จุดบกพร่องของ jQuery ถูกปิดเนื่องจากไม่สามารถแก้ไขได้ นี่คือการออกแบบ

ซึ่งแตกต่างจากคำขอทั่วไป (ที่กล่าวถึงข้างต้น) คำขอ "preflighted" จะส่งคำขอ HTTP โดยวิธี OPTIONS ไปยังทรัพยากรในโดเมนอื่นก่อนเพื่อพิจารณาว่าคำขอจริงนั้นปลอดภัยที่จะส่งหรือไม่ คำขอข้ามไซต์จะมีการระบุไว้ล่วงหน้าเช่นนี้เนื่องจากอาจมีผลกระทบต่อข้อมูลผู้ใช้ โดยเฉพาะอย่างยิ่งคำขอจะถูกระบุไว้ล่วงหน้าหาก:

  • ใช้วิธีอื่นที่ไม่ใช่ GET, HEAD หรือ POST นอกจากนี้หากใช้ POST เพื่อส่งข้อมูลการร้องขอด้วย Content-Type อื่นที่ไม่ใช่ application / x-www-form-urlencoded, multipart / form-data หรือ text / plain เช่นหากคำขอ POST ส่งเพย์โหลด XML ไปยังเซิร์ฟเวอร์ โดยใช้แอปพลิเคชัน / xml หรือข้อความ / xml จากนั้นคำขอจะถูกระบุไว้ล่วงหน้า
  • ตั้งค่าส่วนหัวที่กำหนดเองในคำขอ (เช่นคำขอใช้ส่วนหัวเช่น X-PINGOTHER)

20
ส่วนหัวที่กำหนดเอง นั่นอาจเป็นสิ่งที่ตั้งค่าการเรียกใช้ OPTIONS ล่วงหน้า
Corey Ogburn

18

จากข้อเท็จจริงที่ว่าคำขอไม่ได้ถูกส่งบนพอร์ตเริ่มต้น 80/443 การเรียก Ajax นี้ถือว่าเป็นการร้องขอทรัพยากรข้ามแหล่งกำเนิด (CORS)โดยอัตโนมัติซึ่งหมายความว่าคำขอจะออกคำขอ OPTIONS โดยอัตโนมัติซึ่งจะตรวจสอบ CORS ส่วนหัวที่ด้านของเซิร์ฟเวอร์ / servlet

สิ่งนี้เกิดขึ้นแม้ว่าคุณจะตั้งค่าไว้ก็ตาม

crossOrigin: false;

หรือแม้ว่าคุณจะยอมรับมัน

localhost != localhost:57124เหตุผลก็คือว่า ลองส่งไปlocalhostโดยไม่มีพอร์ตเท่านั้น - จะล้มเหลวเนื่องจากเป้าหมายที่ร้องขอจะไม่สามารถเข้าถึงได้อย่างไรก็ตามโปรดสังเกตว่าหากชื่อโดเมนเท่ากับคำขอจะถูกส่งโดยไม่มีคำขอ OPTIONS ก่อน POST


3

ฉันเห็นด้วยกับ Kevin B รายงานข้อผิดพลาดกล่าวทั้งหมด ดูเหมือนว่าคุณกำลังพยายามโทรผ่าน ajax ข้ามโดเมน หากคุณไม่คุ้นเคยกับนโยบายกำเนิดเดียวกันคุณสามารถเริ่มต้นที่นี่: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript

หากไม่ได้ตั้งใจจะเป็นการโทร ajax ข้ามโดเมนให้ลองกำหนด url เป้าหมายให้สัมพันธ์กันและดูว่าปัญหาจะหายไปหรือไม่ หากคุณสิ้นหวังจริงๆใน JSONP แต่ระวังการทำร้ายร่างกายแฝงตัว ไม่มีอะไรให้เราช่วยคุณได้อีกแล้ว


1
โครงสร้างระบบของเราเป็นสิ่งที่ฉันไม่สามารถเปลี่ยนแปลงได้ การใช้พอร์ตอื่นเป็นข้อกำหนดของสถาปัตยกรรมของเรา ฉันได้รับนโยบายต้นกำเนิดเดียวกัน แต่คิดว่า CORS ที่เรานำมาใช้นั้นเพียงพอแล้ว ชัดเจนว่าไม่.
Corey Ogburn

2
หากเซิร์ฟเวอร์ของคุณส่งคืนการตอบกลับ JSON คุณสามารถดูวิธี JSONP ได้เพียงใช้อย่างรับผิดชอบ
jgitter

1
ฉันไม่สนใจที่จะโต้เถียงกับคุณ แต่ JSONP ใช้แท็กสคริปต์เพื่อดึงข้อมูลจากโดเมนอื่นจากนั้นส่งผลลัพธ์ไปยังฟังก์ชันเรียกกลับ มันยากกว่ามากถ้าผลลัพธ์ไม่ใช่ json
jgitter

1
ไม่มันไม่ได้ยากมากมาย ในความเป็นจริงการตอบกลับไม่ควรเป็น JSON ที่ถูกต้องไม่ว่าในกรณีใด ๆ callbackfunc(somedata)แต่เซิร์ฟเวอร์ควรกลับอะไรเช่นนี้ อย่างที่คุณเห็นนี่ไม่ใช่ JSON ที่ถูกต้อง และsomedataอาจเป็นสตริงหรือตัวเลขหรืออะไรก็ได้ที่คุณต้องการให้เป็น
Ray Nicholus

1
ฉันใช้ Postman และมีวิธีการส่งคำขออย่างถูกต้อง (เช่น 'PUT', 'DELETE' ฯลฯ ) แต่เมื่อฉันพยายามทำจากรหัสของฉันมันมักจะส่งด้วยวิธีการร้องขอ OPTIONS ฉันไม่รู้ว่าบุรุษไปรษณีย์ทำได้อย่างไร
ErwinGO

1

หากเป็นไปได้ให้ส่งพารามิเตอร์ผ่าน GET / POST ปกติด้วยชื่ออื่นและปล่อยให้โค้ดฝั่งเซิร์ฟเวอร์ของคุณจัดการ

ฉันมีปัญหาคล้ายกันกับพร็อกซีของตัวเองในการข้าม CORS และฉันได้รับข้อผิดพลาดเดียวกันกับ POST-> OPTION ใน Chrome มันเป็นAuthorizationส่วนหัวในกรณีของฉัน ( "x-li-format"และ"X-UserName"ในกรณีของคุณ) ฉันลงเอยด้วยการส่งในรูปแบบจำลอง (เช่นAuthorizatinJackใน GET) และฉันเปลี่ยนรหัสสำหรับพร็อกซีของฉันเพื่อเปลี่ยนเป็นส่วนหัวเมื่อโทรไปยังปลายทาง . นี่คือ PHP:

if (isset($_GET['AuthorizationJack'])) {
    $request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}

1

ในกรณีของฉันฉันกำลังเรียก API ที่โฮสต์โดย AWS (API Gateway) ข้อผิดพลาดเกิดขึ้นเมื่อฉันพยายามเรียก API จากโดเมนอื่นที่ไม่ใช่โดเมนของ API ตั้งแต่ฉันเจ้าของ API ที่ฉันเปิดใช้ ธ สำหรับสภาพแวดล้อมการทดสอบตามที่อธิบายไว้ในเอกสาร Amazon

ในการใช้งานจริงข้อผิดพลาดนี้จะไม่เกิดขึ้นเนื่องจากคำขอและ API จะอยู่ในโดเมนเดียวกัน

ฉันหวังว่ามันจะช่วยได้!


0

ในฐานะที่ตอบโดย @Dark เหยี่ยวผมก็จัดการกับมัน

ในกรณีของฉันฉันกำลังใช้เซิร์ฟเวอร์ node.js และสร้างเซสชันหากไม่มีอยู่ เนื่องจากเมธอด OPTIONS ไม่มีรายละเอียดเซสชันในนั้นจึงลงเอยด้วยการสร้างเซสชันใหม่สำหรับทุกคำขอของเมธอด POST

ดังนั้นในกิจวัตรแอปของฉันในการสร้างเซสชันถ้าไม่มีอยู่ฉันเพิ่งเพิ่มการตรวจสอบเพื่อดูว่าวิธีการเป็นOPTIONSอย่างไรและถ้าเป็นเช่นนั้นเพียงข้ามส่วนการสร้างเซสชัน:

    app.use(function(req, res, next) {
        if (req.method !== "OPTIONS") {
            if (req.session && req.session.id) {
                 // Session exists
                 next();
            }else{
                 // Create session
                 next();
          }
        } else {
           // If request method is OPTIONS, just skip this part and move to the next method.
           next(); 
        }
    }

0

คำขอ "preflighted" ก่อนจะส่งคำขอ HTTP โดยวิธี OPTIONS ไปยังทรัพยากรในโดเมนอื่นเพื่อพิจารณาว่าคำขอจริงนั้นปลอดภัยที่จะส่งหรือไม่ คำขอข้ามไซต์

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


1
คุณช่วยเพิ่มข้อมูลอีกเล็กน้อยได้ไหม คำตอบของคุณดูเหมือนความคิดเห็น :)
Badacadabra

0

พิจารณาใช้axios

axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {

  if(res.data.error) {

  } else { 
    doAnything( res.data )
  }

}).catch(function (error) {
   doAnythingError(error)
});

ฉันมีปัญหานี้โดยใช้การดึงข้อมูลและแกนทำงานได้อย่างสมบูรณ์


5
Axios ยังใช้ OPTIONS แรก
Skylin R

0

ฉันพบปัญหาที่คล้ายกันมาก ฉันใช้เวลาเกือบครึ่งวันเพื่อทำความเข้าใจว่าทำไมทุกอย่างทำงานได้อย่างถูกต้องใน Firefox และล้มเหลวใน Chrome ในกรณีของฉันมันเป็นเพราะฟิลด์ที่ซ้ำกัน (หรืออาจพิมพ์ผิด) ในส่วนหัวคำขอของฉัน


0

ใช้การดึงข้อมูลแทน XHR จากนั้นคำขอจะไม่ถูกนำเสนอล่วงหน้าแม้ว่าจะข้ามโดเมนก็ตาม


-1
 $.ajax({
            url: '###',
            contentType: 'text/plain; charset=utf-8',
            async: false,
            xhrFields: {
                withCredentials: true,
                crossDomain: true,
                Authorization: "Bearer ...."
            },

            method: 'POST',

            data: JSON.stringify( request ),
            success: function (data) {
                console.log(data);
            }
        });

contentType: 'ข้อความ / ธรรมดา; charset = utf-8 'หรือเพียงแค่ contentType:' text / plain 'ใช้ได้กับฉัน! ความนับถือ!!


สิ่งนี้เกี่ยวข้องกับคำถามทั้งหมดหรือไม่?
Corey Ogburn

สวัสดีฉันคิดว่าวิธีนี้ช่วยแก้ปัญหาในชื่อได้ด้วยเนื้อหาประเภทนี้คุณผ่านวิธี OPTIONS ขอแสดงความนับถือ
David Lopes

ContentType ไม่มีส่วนเกี่ยวข้องกับเมธอด
Corey Ogburn

ฉันรู้ว่าคุณกำลังพูดอะไร แต่ลองดูสิ ขึ้นอยู่กับเบราว์เซอร์ประเภทเนื้อหาของคุณสามารถมีผลต่อคำขอของคุณและเปลี่ยนวิธีการของคุณ!
David Lopes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.