วิธีจัดการคำขอเปลี่ยนเส้นทางหลังจากการโทร jQuery Ajax


1368

ฉันใช้$.post()โทร servlet โดยใช้ Ajax แล้วใช้ส่วน HTML ที่เป็นผลลัพธ์เพื่อแทนที่divองค์ประกอบในหน้าปัจจุบันของผู้ใช้ อย่างไรก็ตามหากเซสชันหมดเวลาเซิร์ฟเวอร์จะส่งคำสั่งเปลี่ยนเส้นทางเพื่อส่งผู้ใช้ไปยังหน้าเข้าสู่ระบบ ในกรณีนี้ jQuery กำลังแทนที่divองค์ประกอบด้วยเนื้อหาของหน้าเข้าสู่ระบบบังคับให้ผู้ใช้มองเห็นฉากที่หายากแน่นอน

ฉันจะจัดการคำสั่งเปลี่ยนเส้นทางจากการโทร Ajax ด้วย jQuery 1.2.6 ได้อย่างไร


1
(ไม่ใช่คำตอบเช่นนี้) - ฉันเคยทำสิ่งนี้มาแล้วในอดีตโดยการแก้ไขไลบรารี jquery และเพิ่มการตรวจสอบสำหรับหน้าเข้าสู่ระบบในแต่ละ XHR ให้เสร็จสมบูรณ์ ไม่ใช่ทางออกที่ดีที่สุดเพราะจะต้องทำทุกครั้งที่คุณอัพเกรด แต่มันจะแก้ปัญหาได้
Sugendran

1
ดูคำถามที่เกี่ยวข้อง: stackoverflow.com/questions/5941933/…
Nutel

HttpContext.Response.AddHeaderและตรวจสอบที่สำเร็จ ajaxSetup เป็นวิธีที่จะไป
LCJ

4
เหตุใดจึงไม่ส่งคืนเซิร์ฟเวอร์ 401 ในกรณีดังกล่าวคุณสามารถมี $ .ajaxSetup ทั่วโลกและใช้รหัสสถานะเพื่อเปลี่ยนเส้นทางหน้า
Vishal

1
ลิงค์นี้doanduyhai.wordpress.com/2012/04/21/…ให้ทางออกที่ถูกต้องแก่ฉัน
pappu_kutty

คำตอบ:


697

ฉันอ่านคำถามนี้และใช้วิธีการที่ระบุไว้เกี่ยวกับการตั้งค่ารหัสสถานะ HTTPการตอบสนองเป็น 278 เพื่อหลีกเลี่ยงเบราว์เซอร์ที่จัดการการเปลี่ยนเส้นทางอย่างโปร่งใส แม้ว่ามันจะใช้ได้ดี แต่ฉันก็ไม่พอใจเล็กน้อยเพราะมันเป็นบิตของการแฮ็ค

หลังจากขุดอีกรอบผมทิ้งวิธีนี้และใช้JSON ในกรณีนี้การตอบสนองต่อการร้องขอ AJAX ทั้งหมดมีรหัสสถานะ 200 และเนื้อหาของการตอบสนองมีวัตถุ JSON ที่สร้างขึ้นบนเซิร์ฟเวอร์ JavaScript บนไคลเอนต์สามารถใช้วัตถุ JSON เพื่อตัดสินใจว่าจะต้องทำอะไร

ฉันมีปัญหาคล้ายกับของคุณ ฉันทำการร้องขอ AJAX ที่มี 2 คำตอบที่เป็นไปได้: อันที่เปลี่ยนเส้นทางเบราว์เซอร์ไปยังหน้าใหม่และอีกอันที่แทนที่รูปแบบ HTML ที่มีอยู่ในหน้าปัจจุบันด้วยหน้าใหม่ รหัส jQuery ที่ทำสิ่งนี้ดูเหมือนว่า:

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        } else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

JSON วัตถุ "ข้อมูล" ที่สร้างขึ้นบนเซิร์ฟเวอร์จะมี 2 สมาชิก: และdata.redirect data.formฉันพบวิธีการนี้จะดีขึ้นมาก


58
ตามที่ระบุไว้ในวิธีแก้ปัญหาในstackoverflow.com/questions/503093/…จะดีกว่าถ้าใช้ window.location.replace (data.redirect) กว่า window.location.href = data.redirect;
Carles Barrobés

8
ไม่ว่าด้วยเหตุผลใดมันจะไม่เป็นการดีกว่าถ้าจะใช้รหัส HTTP กับการกระทำ ตัวอย่างเช่นรหัส 307 ซึ่งเป็น HTTP Temporary Redirect?
Sergei Golos

15
@Sergei Golos เหตุผลก็คือถ้าคุณทำการเปลี่ยนเส้นทาง HTTP การเปลี่ยนเส้นทางจะไม่มาถึงการเรียกกลับสำเร็จ ajax เบราว์เซอร์จะประมวลผลการเปลี่ยนเส้นทางโดยส่งรหัส 200 พร้อมเนื้อหาของปลายทางของการเปลี่ยนเส้นทาง
Miguel Silva

2
รหัสเซิร์ฟเวอร์จะมีลักษณะอย่างไร เพื่อให้ค่าส่งคืนของ data.form และ data.redirect โดยทั่วไปฉันจะตรวจสอบได้อย่างไรว่าฉันเปลี่ยนเส้นทางในมัน?
Vnge

6
คำตอบนี้จะเป็นประโยชน์มากขึ้นถ้ามันแสดงให้เห็นว่าสิ่งนี้จะทำบนเซิร์ฟเวอร์
Pedro Hoehl Carvalho

243

ฉันแก้ไขปัญหานี้โดย:

  1. การเพิ่มส่วนหัวที่กำหนดเองในการตอบสนอง:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
  2. เชื่อมโยงฟังก์ชัน JavaScript กับajaxSuccessเหตุการณ์และตรวจสอบเพื่อดูว่ามีส่วนหัวหรือไม่:

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });

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

4
ฉันเพิ่งทำสิ่งนี้และพบว่าฉันต้องการ ajaxComplete โดยที่ฉันใช้ฟังก์ชัน $ .get () และสถานะอื่น ๆ ที่ไม่ใช่ 200 ไม่ได้เป็นการยิง ที่จริงแล้วฉันอาจจะเพิ่งผูกกับ ajaxError แทน ดูคำตอบของฉันด้านล่างสำหรับรายละเอียดเพิ่มเติม
Bretticus

2
ไม่เป็นไรในหลายกรณี แต่ถ้ากรอบงานของคุณจัดการการอนุญาต
jwaliszko

13
ฉันชอบวิธีส่วนหัว แต่ก็คิดว่า - เช่น @ mwoods79 - ที่ความรู้ว่าจะเปลี่ยนเส้นทางไปยังที่ใดควรทำซ้ำ ฉันแก้ไขได้โดยเพิ่มหัวข้อ REDIRECT_LOCATION แทนที่จะเป็นบูลีน
rintcius

3
ระมัดระวังในการตั้งค่าส่วนหัวในการตอบสนองหลังจากเปลี่ยนเส้นทาง ตามที่อธิบายไว้ในคำตอบอื่น ๆ ในหน้านี้การเปลี่ยนเส้นทางอาจโปร่งใสไปยังตัวจัดการ ajaxSucces ดังนั้นฉันจึงรวมส่วนหัวในการตอบสนอง GET ของหน้าเข้าสู่ระบบ (ซึ่งในที่สุดก็เป็นเพียงการเรียก ajaxSuccess ในสถานการณ์ของฉัน)
sieppl

118

ไม่มีเบราว์เซอร์ที่รองรับการตอบสนอง 301 และ 302 อย่างถูกต้อง และในความเป็นจริงมาตรฐานก็บอกว่าพวกเขาควรจัดการกับพวกเขา "โปร่งใส" ซึ่งเป็นอาการปวดหัวอย่างมากสำหรับผู้ขายอาแจ็กซ์ห้องสมุด ในRa-Ajaxเราถูกบังคับให้ใช้รหัสสถานะการตอบสนอง HTTP 278 (เพียงบางรหัสความสำเร็จ "ไม่ได้ใช้") เพื่อจัดการการเปลี่ยนเส้นทางโปร่งใสจากเซิร์ฟเวอร์ ...

สิ่งนี้ทำให้ฉันรำคาญจริง ๆ และถ้าใครบางคนที่นี่มี "pull" ใน W3C ฉันจะขอบคุณที่คุณสามารถให้ W3C รู้ว่าเราจำเป็นต้องจัดการ 301 และ 302 รหัสตัวเอง ... ! ;)


3
สำหรับหนึ่งครั้งที่ 278 ควรแยกจาก HTTP Spec อย่างเป็นทางการ
Chris Marisic

2
มันไม่ได้จัดการกับพวกเขาอย่างโปร่งใสหรือไม่? หากมีการย้ายทรัพยากรการจัดการมันอย่างโปร่งใสหมายถึงการทำซ้ำการร้องขอใน URL ที่ให้ไว้ นั่นคือสิ่งที่ฉันคาดหวังจากการใช้ XMLHttpRequest API
Philippe Rathé

@ PhilippeRathéเห็นด้วย การจัดการที่โปร่งใสเป็นสิ่งที่ฉันต้องการ และฉันไม่รู้ว่าทำไมมันถึงไม่ดี
smwikipedia

@smwikipedia เพื่อจัดการการเปลี่ยนเส้นทางของมาร์กอัปในส่วนหลักโดยไม่เปลี่ยนเส้นทางหน้าเว็บ
cmc

ถ้าใครบางคนที่นี่มี "pull" ใน W3C ฉันขอขอบคุณที่คุณสามารถแจ้งให้ W3C รู้ว่าเราจำเป็นต้องจัดการ 301 และ 302 รหัสตัวเอง ... ! ;)
Tommy.Tang

100

วิธีแก้ปัญหาที่นำมาใช้ในที่สุดก็คือการใช้ wrapper สำหรับฟังก์ชั่นการโทรกลับของการโทร Ajax และในการตรวจสอบห่อหุ้มนี้สำหรับการดำรงอยู่ขององค์ประกอบที่เฉพาะเจาะจงใน HTML อันกลับมา หากพบองค์ประกอบแล้ว wrapper จะทำการเปลี่ยนเส้นทาง ถ้าไม่ได้ wrapper จะส่งต่อการเรียกไปยังฟังก์ชั่นการโทรกลับจริง

ตัวอย่างเช่นฟังก์ชั่น wrapper ของเราเป็นเช่น:

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

จากนั้นเมื่อทำการโทร Ajax เราใช้สิ่งที่ชอบ:

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

สิ่งนี้ใช้ได้กับเราเพราะการโทร Ajax ทั้งหมดจะส่งคืน HTML ภายในองค์ประกอบ DIV ที่เราใช้เพื่อแทนที่ส่วนหนึ่งของหน้า นอกจากนี้เราเพียงต้องการเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ


4
โปรดทราบว่าสิ่งนี้สามารถย่อให้ทำงาน cbWrapper (ฟังก์ชั่น) {ฟังก์ชันคืน (ข้อมูล) {ถ้า ($ ("# myForm", data) .size ()> 0) top.location.href = "เข้าสู่ระบบ"; ฟังก์ชันอื่น (ข้อมูล); }} จากนั้นคุณจะต้องใช้ cbWrapper (myActualCB) เมื่อโทรหา. ใช่รหัสในการแสดงความคิดเห็นเป็นระเบียบ แต่มันควรจะตั้งข้อสังเกต :)
Simen Echholt

ขนาดถูกคิดค่าเสื่อมราคาเพื่อให้คุณสามารถใช้ความยาวได้ที่นี่แทนขนาด
นิล

66

ฉันชอบวิธีของ Timmerz ด้วยการบีบมะนาวเล็กน้อย ถ้าคุณเคยได้รับกลับcontentTypeของtext / htmlเมื่อคุณคาดหวังJSON , คุณมักจะถูกนำไป ในกรณีของฉันฉันเพียงแค่โหลดหน้าใหม่และมันจะถูกเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ โอ้และตรวจสอบว่าสถานะ jqXHR เป็น 200 ซึ่งดูเหมือนว่าโง่เพราะคุณอยู่ในฟังก์ชั่นข้อผิดพลาดใช่ไหม? มิฉะนั้นกรณีข้อผิดพลาดที่ถูกกฎหมายจะบังคับให้โหลดซ้ำซ้ำ (อุ๊ปส์)

$.ajax(
   error:  function (jqXHR, timeout, message) {
    var contentType = jqXHR.getResponseHeader("Content-Type");
    if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
        // assume that our login has expired - reload our current page
        window.location.reload();
    }

});

1
ขอบคุณมาก Brian คำตอบของคุณดีที่สุดสำหรับสถานการณ์ของฉันแม้ว่าฉันต้องการหากมีการตรวจสอบที่ปลอดภัยกว่าเช่นการเปรียบเทียบ URL / หน้าใดที่เปลี่ยนเส้นทางไปที่แทนที่จะใช้การตรวจสอบ "ประเภทเนื้อหา" แบบง่าย ฉันไม่พบหน้าใดที่ถูกเปลี่ยนเส้นทางไปจากวัตถุ jqXHR
Johnny

ฉันตรวจสอบสถานะ 401 แล้วเปลี่ยนเส้นทาง ทำงานเหมือนแชมป์
Eric

55

ใช้การ$.ajax()โทรระดับต่ำ:

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

ลองใช้วิธีนี้ในการเปลี่ยนเส้นทาง:

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}

ฉันพยายามหลีกเลี่ยงสิ่งที่อยู่ในระดับต่ำ อย่างไรก็ตามสมมติว่าฉันใช้สิ่งที่คุณอธิบายฉันจะบังคับให้เปลี่ยนเส้นทางเบราว์เซอร์ได้อย่างไรเมื่อตรวจพบว่ารหัส HTTP เป็น 3xx เป้าหมายของฉันคือการเปลี่ยนเส้นทางผู้ใช้ไม่ใช่เพียงเพื่อประกาศว่าเซสชันของตนหมดอายุแล้ว
Elliot Vargas

4
Btw, $ .ajax () ไม่ใช่ระดับต่ำมาก เป็นเพียงระดับต่ำในแง่ของ jQuery เพราะมี $ .get, $. โพสต์ ฯลฯ ซึ่งง่ายกว่า $ .ajax และตัวเลือกทั้งหมด
จนถึง

16
โอ้เด็ก! ขออภัยฉันต้อง "ยกเลิกการยอมรับ" คำตอบของคุณมันยังมีประโยชน์มาก สิ่งนั้นคือการเปลี่ยนเส้นทางได้รับการจัดการโดยอัตโนมัติโดย XMLHttpRequest ดังนั้นฉันมักจะได้รับรหัสสถานะ 200 หลังจากการเปลี่ยนเส้นทาง (ถอนหายใจ!) ฉันคิดว่าฉันจะต้องทำสิ่งที่น่ารังเกียจเช่นการแยกวิเคราะห์ HTML และมองหาเครื่องหมาย
Elliot Vargas

1
แค่สงสัยถ้าเซสชั่นจบที่เซิร์ฟเวอร์นั่นไม่ได้หมายความว่าเซิร์ฟเวอร์ส่ง SESSIONID ที่แตกต่างกัน? เราตรวจไม่ได้เหรอ?
Salamander2007

10
หมายเหตุ: วิธีนี้ใช้ไม่ได้กับการเปลี่ยนเส้นทาง ajax จะไปที่หน้าใหม่และส่งคืนรหัสสถานะ

33

ฉันแค่อยากจะแบ่งปันแนวทางของฉันเพราะมันอาจช่วยคนได้:

ฉันโดยทั่วไปรวมโมดูล JavaScript ที่จัดการสิ่งที่รับรองความถูกต้องเช่นการแสดงชื่อผู้ใช้และกรณีนี้การจัดการการเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบการเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ

สถานการณ์ของฉัน: โดยทั่วไปเรามีเซิร์ฟเวอร์ ISA ซึ่งจะรับฟังคำขอทั้งหมดและตอบกลับด้วย 302 และส่วนหัวของที่ตั้งในหน้าเข้าสู่ระบบของเรา

ในโมดูล JavaScript ของฉันวิธีการเริ่มต้นของฉันเป็นเหมือน

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

ปัญหา (ดังที่ได้กล่าวไว้แล้ว) คือเบราว์เซอร์จะจัดการการเปลี่ยนเส้นทางด้วยตัวเองดังนั้นการajaxCompleteโทรกลับของฉันไม่เคยถูกเรียก แต่ฉันกลับได้รับการตอบกลับของหน้าล็อกอินที่เปลี่ยนเส้นทางแล้วซึ่งเห็นได้ชัดว่าเป็นstatus 200ซึ่งเห็นได้ชัดว่าเป็นปัญหา: คุณจะตรวจสอบอย่างไรว่าการตอบสนอง 200 ครั้งที่ประสบความสำเร็จเป็นหน้าเข้าสู่ระบบจริงของคุณหรือหน้าอื่น ๆ โดยพลการ ??

การแก้ไขปัญหา

เนื่องจากฉันไม่สามารถรับการตอบสนองการเปลี่ยนเส้นทางได้ 302 ครั้งฉันจึงเพิ่มLoginPageส่วนหัวในหน้าเข้าสู่ระบบซึ่งมี URL ของหน้าเข้าสู่ระบบเอง ในโมดูลตอนนี้ฉันฟังส่วนหัวและทำการเปลี่ยนเส้นทาง:

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

... และนั่นใช้งานได้อย่างมีเสน่ห์ :) คุณอาจสงสัยว่าทำไมฉันจึงรวม URL ไว้ในLoginPageส่วนหัว ... โดยทั่วไปเพราะฉันไม่พบวิธีการกำหนด URL ที่เป็นGETผลมาจากการเปลี่ยนเส้นทางตำแหน่งอัตโนมัติจากxhrวัตถุ ...


1 - แต่ส่วนหัวที่กำหนดเองที่ควรจะเริ่มต้นด้วยเพื่อให้หัวดีกว่าที่จะใช้จะเป็นX- X-LoginPage: http://example.com/login
uınbɐɥs

6
@ShaquinTrifonoff ไม่มีอีกแล้ว ฉันไม่ได้ใช้คำนำหน้า X- เพราะในเดือนมิถุนายน 2554เอกสาร ITEF เสนอการคัดค้านและแน่นอนด้วยมิถุนายน 2012ไม่มีทางที่หัวกระดาษที่กำหนดเองไม่ควรขึ้นต้นด้วยX-อีก
Juri

เรายังมีเซิร์ฟเวอร์ ISA และฉันเพิ่งพบปัญหาเดียวกัน แทนที่จะใช้รหัสนี้เราใช้คำแนะนำในkb2596444เพื่อกำหนดค่า ISA เพื่อหยุดการเปลี่ยนเส้นทาง
กอตต์สโตน

29

ฉันรู้ว่าเรื่องนี้เป็นรุ่นเก่า แต่ฉันจะให้ยังอีกวิธีหนึ่งที่ฉันได้พบและอธิบายไว้ก่อนหน้านี่ โดยทั่วไปฉันใช้ ASP.MVC กับ WIF (แต่นี่ไม่สำคัญสำหรับบริบทของหัวข้อนี้ - คำตอบนั้นเพียงพอไม่ว่าจะใช้เฟรมเวิร์กใดเบาะแสยังคงไม่เปลี่ยนแปลง - จัดการกับปัญหาที่เกี่ยวข้องกับความล้มเหลวในการรับรองความถูกต้อง ) .

วิธีการที่แสดงด้านล่างสามารถนำไปใช้กับคำขอ ajax ทั้งหมดออกจากกล่อง (หากพวกเขาไม่ได้กำหนดใหม่ก่อนที่จะส่งเหตุการณ์ที่เห็นได้ชัด)

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

ก่อนที่จะทำการร้องขอ ajax ใด ๆ จะCheckPulseถูกเรียกใช้วิธีการ (วิธีการควบคุมซึ่งสามารถเป็นอะไรที่ง่ายที่สุด):

[Authorize]
public virtual void CheckPulse() {}

หากผู้ใช้ไม่ได้รับการรับรองความถูกต้อง (โทเค็นหมดอายุ) วิธีการดังกล่าวไม่สามารถเข้าถึงได้ (ป้องกันโดยAuthorizeคุณลักษณะ) เนื่องจากเฟรมเวิร์กจัดการการพิสูจน์ตัวตนในขณะที่โทเค็นหมดอายุจึงทำให้สถานะ http เป็น 302 ในการตอบสนอง หากคุณไม่ต้องการให้เบราว์เซอร์จัดการกับการตอบสนอง 302 อย่างโปร่งใสให้จับใน Global.asax และเปลี่ยนสถานะการตอบกลับ - เช่น 200 OK นอกจากนี้ให้เพิ่มส่วนหัวซึ่งแนะนำให้คุณประมวลผลการตอบสนองดังกล่าวในลักษณะพิเศษ (ภายหลังที่ด้านลูกค้า):

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

ในที่สุดที่ด้านลูกค้าตรวจสอบส่วนหัวที่กำหนดเองดังกล่าว หากปัจจุบัน - การเปลี่ยนเส้นทางไปยังหน้าการเข้าสู่ระบบแบบเต็มควรทำ (ในกรณีของฉันwindow.locationถูกแทนที่ด้วย url จากคำขอซึ่งจัดการโดยอัตโนมัติโดยกรอบงานของฉัน)

function checkPulse(XMLHttpRequest) {
    var location = window.location.href;
    $.ajax({
        url: "/Controller/CheckPulse",
        type: 'GET',
        async: false,
        beforeSend: null,
        success:
            function (result, textStatus, xhr) {
                if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
                    XMLHttpRequest.abort(); // terminate further ajax execution
                    window.location = location;
                }
            }
    });
}

ฉันแก้ไขปัญหาโดยใช้เหตุการณ์ PostAuthenticateRequest แทนเหตุการณ์ EndRequest
Frinavale

@JaroslawWaliszko ฉันวางเหตุการณ์ที่ผิดไปในคำตอบสุดท้ายของฉัน! ฉันหมายถึงเหตุการณ์ PreSendRequestHeaders ..... ไม่ใช่ PostAuthenticateRequest! >> หน้าแดง << ขอบคุณที่ชี้ให้เห็นความผิดพลาดของฉัน
Frinavale

@JaroslawWaliszko เมื่อใช้ WIF คุณสามารถส่งคืนการตอบกลับ 401 คำขอ AJAX และให้ javascript ของคุณจัดการกับสิ่งเหล่านี้ นอกจากนี้คุณกำลังสมมติว่า 302 ทั้งหมดต้องการการรับรองความถูกต้องซึ่งอาจไม่เป็นจริงในทุกกรณี ฉันได้เพิ่มคำตอบหากใครสนใจ
Rob

26

ผมคิดว่าเป็นวิธีที่ดีในการจัดการนี้คือการใช้ประโยชน์จากรหัสการตอบสนองโปรโตคอล HTTP 401 Unauthorizedที่มีอยู่โดยเฉพาะ

นี่คือวิธีที่ฉันแก้ไขมัน:

  1. ฝั่งเซิร์ฟเวอร์: ถ้าเซสชันหมดอายุและคำขอคือ ajax ส่งส่วนหัวรหัสตอบกลับ 401
  2. ฝั่งไคลเอ็นต์: เชื่อมโยงกับเหตุการณ์ ajax

    $('body').bind('ajaxSuccess',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    }).bind('ajaxError',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    });

IMO นี้เป็นแบบทั่วไปมากขึ้นและคุณไม่ได้เขียนข้อมูลจำเพาะ / ส่วนหัวที่กำหนดเองใหม่ คุณไม่ควรแก้ไขการโทร ajax ใด ๆ ที่คุณมีอยู่

แก้ไข:ตามความคิดเห็นของ @ Rob ด้านล่าง 401 (รหัสสถานะ HTTP สำหรับข้อผิดพลาดการตรวจสอบ) ควรเป็นตัวบ่งชี้ ดู403 Forbidden VS 401 ตอบสนอง HTTP ไม่ได้รับอนุญาตสำหรับรายละเอียดเพิ่มเติม เมื่อกล่าวถึงกรอบงานเว็บบางอันใช้ 403 สำหรับทั้งข้อผิดพลาดในการตรวจสอบสิทธิ์และการอนุญาตดังนั้นควรปรับเปลี่ยนให้เหมาะสม ขอบคุณ Rob


ฉันใช้วิธีการเดียวกัน jQuery โทร ajaxSuccess จริง ๆ กับรหัสข้อผิดพลาด 403 หรือไม่ ฉันคิดว่าจำเป็นต้องใช้ส่วน ajaxError จริง ๆ เท่านั้น
Marius Balčytis

24

ฉันแก้ไขปัญหานี้เช่นนี้:

เพิ่มมิดเดิลแวร์เพื่อประมวลผลการตอบสนองหากเป็นการเปลี่ยนเส้นทางสำหรับการร้องขอ ajax ให้เปลี่ยนการตอบกลับเป็นการตอบสนองปกติด้วย url การเปลี่ยนเส้นทาง

class AjaxRedirect(object):
  def process_response(self, request, response):
    if request.is_ajax():
      if type(response) == HttpResponseRedirect:
        r = HttpResponse(json.dumps({'redirect': response['Location']}))
        return r
    return response

จากนั้นใน ajaxComplete หากการตอบกลับมีการเปลี่ยนเส้นทางมันจะต้องเป็นการเปลี่ยนเส้นทางดังนั้นให้เปลี่ยนตำแหน่งของเบราว์เซอร์

$('body').ajaxComplete(function (e, xhr, settings) {
   if (xhr.status == 200) {
       var redirect = null;
       try {
           redirect = $.parseJSON(xhr.responseText).redirect;
           if (redirect) {
               window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
           }
       } catch (e) {
           return;
       }
   }
}

20

ฉันมีวิธีง่ายๆที่เหมาะกับฉันไม่ต้องเปลี่ยนรหัสเซิร์ฟเวอร์ ... เพียงแค่เพิ่มลูกจันทน์เทศช้อนชา ...

$(document).ready(function ()
{
    $(document).ajaxSend(
    function(event,request,settings)
    {
        var intercepted_success = settings.success;
        settings.success = function( a, b, c ) 
        {  
            if( request.responseText.indexOf( "<html>" ) > -1 )
                window.location = window.location;
            else
                intercepted_success( a, b, c );
        };
    });
});

ฉันตรวจสอบสถานะของแท็ก html แต่คุณสามารถเปลี่ยน indexOf เพื่อค้นหาสตริงเฉพาะที่มีอยู่ในหน้าเข้าสู่ระบบของคุณ ...


ดูเหมือนว่ามันจะไม่เหมาะกับฉัน แต่มันจะเรียกฟังก์ชั่นที่กำหนดด้วย ajax call เหมือนว่ามันจะไม่เอาชนะวิธีการสำเร็จ
adriaanp

ดูเหมือนจะไม่ได้ผลสำหรับฉันอย่างน้อยอีกต่อไป คำอธิบายที่เป็นไปได้ที่นี่: stackoverflow.com/a/12010724/260665
Raj Pawan Gumdal

20

วิธีการแก้ปัญหาที่ผมพบ (มีประโยชน์โดยเฉพาะอย่างยิ่งถ้าคุณต้องการที่จะกำหนดพฤติกรรมทั่วโลก) คือการใช้$.ajaxsetup()วิธีการร่วมกับสถานที่ให้บริการstatusCode เช่นเดียวกับที่คนอื่น ๆ ชี้ให้เห็นอย่าใช้รหัสสถานะการเปลี่ยนเส้นทาง ( 3xx) แทนที่จะใช้รหัส4xxสถานะและจัดการกับฝั่งไคลเอ็นต์ของการเปลี่ยนเส้นทาง

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

แทนที่400ด้วยรหัสสถานะที่คุณต้องการจัดการ เช่นเดียวกับที่กล่าวถึงแล้ว401 Unauthorizedอาจเป็นความคิดที่ดี ฉันใช้ตัวเลือกนี้400เนื่องจากไม่เจาะจงมากและฉันสามารถใช้401สำหรับกรณีเฉพาะเพิ่มเติม (เช่นข้อมูลรับรองการเข้าสู่ระบบที่ไม่ถูกต้อง) ดังนั้นแทนที่จะเปลี่ยนเส้นทางโดยตรงแบ็กเอนด์ของคุณควรส่งคืน4xxรหัสข้อผิดพลาดเมื่อเซสชันหมดเวลาและคุณจัดการกับการเปลี่ยนเส้นทางฝั่งไคลเอ็นต์ ทำงานได้สมบูรณ์แบบสำหรับฉันแม้จะใช้เฟรมเวิร์กอย่าง backbone.js


จะพูดถึงฟังก์ชั่นในหน้าได้ที่ไหน?
Vikrant

@Vikrant หากฉันเข้าใจคำถามของคุณถูกต้องคุณสามารถเรียกใช้ฟังก์ชันได้ทันทีหลังจากโหลด jQuery และก่อนที่คุณจะทำตามคำขอจริงของคุณ
morten.c

19

โซลูชันที่ให้มาส่วนใหญ่ใช้วิธีแก้ปัญหาโดยใช้ส่วนหัวพิเศษหรือรหัส HTTP ที่ไม่เหมาะสม โซลูชันเหล่านั้นอาจทำงานได้ แต่รู้สึกว่า 'แฮ็ค' เล็กน้อย ฉันคิดวิธีแก้ปัญหาอื่น

เรากำลังใช้ WIF ซึ่งถูกกำหนดค่าให้เปลี่ยนเส้นทาง (passiveRedirectEnabled = "true") ในการตอบกลับ 401 การเปลี่ยนเส้นทางนั้นมีประโยชน์เมื่อจัดการคำขอปกติ แต่จะไม่ทำงานสำหรับคำขอ AJAX (เนื่องจากเบราว์เซอร์จะไม่ดำเนินการเปลี่ยนเส้นทาง 302 /)

การใช้รหัสต่อไปนี้ใน global.asax ของคุณคุณสามารถปิดการใช้งานการเปลี่ยนเส้นทางสำหรับคำขอ AJAX:

    void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
    {
        string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];

        if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
        {
            e.RedirectToIdentityProvider = false;
        }
    }

วิธีนี้ช่วยให้คุณสามารถส่งกลับการตอบสนอง 401 ครั้งสำหรับคำขอ AJAX ซึ่งจาวาสคริปต์ของคุณสามารถจัดการได้โดยการโหลดหน้าซ้ำ การโหลดหน้าซ้ำจะมี 401 ซึ่ง WIF จะจัดการ (และ WIF จะเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าเข้าสู่ระบบ)

ตัวอย่าง javascript เพื่อจัดการข้อผิดพลาด 401:

$(document).ajaxError(function (event, jqxhr, settings, exception) {

    if (jqxhr.status == 401) { //Forbidden, go to login
        //Use a reload, WIF will redirect to Login
        location.reload(true);
    }
});

ทางออกที่ดี ขอบคุณ
DanMan

19

ปัญหานี้อาจปรากฏขึ้นโดยใช้วิธี ASP.NET MVC RedirectToAction เพื่อป้องกันไม่ให้รูปแบบการแสดงการตอบสนองใน div ที่คุณสามารถทำชนิดของตัวกรองการตอบสนองของอาแจ็กซ์บางอย่างสำหรับการตอบสนองกับ incomming $ .ajaxSetup หากการตอบสนองมีการเปลี่ยนเส้นทาง MVC คุณสามารถประเมินนิพจน์นี้ทางด้าน JS รหัสตัวอย่างสำหรับ JS ด้านล่าง:

$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

หาก data คือ: "window.location = '/ Acount / Login'"ตัวกรองด้านบนจะตรวจจับและประเมินเพื่อทำการเปลี่ยนเส้นทางแทนที่จะปล่อยให้ข้อมูลถูกแสดง


dataอยู่ในเนื้อหาการตอบสนองหรือส่วนหัว?
GMsoF

16

รวบรวมสิ่งที่ Vladimir Prudnikov และ Thomas Hansen กล่าวไว้:

  • เปลี่ยนรหัสฝั่งเซิร์ฟเวอร์ของคุณเพื่อตรวจสอบว่าเป็น XHR หรือไม่ หากเป็นเช่นนั้นให้ตั้งรหัสตอบกลับของการเปลี่ยนเส้นทางเป็น 278 ใน django:
   if request.is_ajax():
      response.status_code = 278

สิ่งนี้ทำให้เบราว์เซอร์ถือว่าการตอบสนองเป็นความสำเร็จและมอบให้กับ Javascript ของคุณ

  • ใน JS ของคุณตรวจสอบให้แน่ใจว่าการส่งแบบฟอร์มผ่าน Ajax ตรวจสอบรหัสตอบกลับและเปลี่ยนเส้นทางหากจำเป็น:
$('#my-form').submit(function(event){ 

  event.preventDefault();   
  var options = {
    url: $(this).attr('action'),
    type: 'POST',
    complete: function(response, textStatus) {    
      if (response.status == 278) { 
        window.location = response.getResponseHeader('Location')
      }
      else { ... your code here ... } 
    },
    data: $(this).serialize(),   
  };   
  $.ajax(options); 
});

13
    <script>
    function showValues() {
        var str = $("form").serialize();
        $.post('loginUser.html', 
        str,
        function(responseText, responseStatus, responseXML){
            if(responseStatus=="success"){
                window.location= "adminIndex.html";
            }
        });     
    }
</script>

12

ลอง

    $(document).ready(function () {
        if ($("#site").length > 0) {
            window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
        }
    });

วางไว้ในหน้าเข้าสู่ระบบ หากมีการโหลดใน div ในหน้าหลักมันจะเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ "#site" เป็นรหัสของ div ซึ่งอยู่ในทุกหน้ายกเว้นหน้าเข้าสู่ระบบ


12

ในขณะที่คำตอบดูเหมือนจะใช้ได้ผลสำหรับผู้คนหากคุณใช้ Spring Security ฉันพบว่ามีการขยาย LoginUrlAuthenticationEntryPoint และเพิ่มรหัสเฉพาะเพื่อจัดการ AJAX ที่มีประสิทธิภาพมากขึ้น ตัวอย่างส่วนใหญ่สกัดกั้นการเปลี่ยนเส้นทางทั้งหมดไม่ใช่แค่การตรวจสอบสิทธิ์ล้มเหลว นี่เป็นสิ่งที่ไม่พึงประสงค์สำหรับโครงการที่ฉันทำงาน คุณอาจพบว่าจำเป็นต้องขยาย ExceptionTranslationFilter และแทนที่เมธอด "sendStartAuthentication" เพื่อลบขั้นตอนการแคชหากคุณไม่ต้องการแคช AJAX ที่ล้มเหลว

ตัวอย่าง AjaxAwareAuthenticationEntryPoint:

public class AjaxAwareAuthenticationEntryPoint extends
    LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if (isAjax(request)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
        } else {
        super.commence(request, response, authException);
        }
    }

    public static boolean isAjax(HttpServletRequest request) {
        return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }
}

แหล่งที่มา: 1 , 2


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

หากใช้ Spring และใช้ JSF ให้ตรวจสอบสิ่งนี้ด้วย: ("partial / ajax"). equalsIgnoreCase (request.getHeader ("faces-request"));
J Slick

ผู้ใช้อาจลงคะแนนเพราะคุณไม่ได้พูดถึง: (1) mods ฝั่งไคลเอ็นต์ที่ต้องการเพื่อตรวจจับการตอบสนองข้อผิดพลาดของคุณ; (2) modisite ที่จำเป็นสำหรับการกำหนดค่าสปริงเพื่อเพิ่ม LoginUrlAuthenticationEntryPoint ที่คุณกำหนดเองให้กับเชนตัวกรอง
J Slick

คำตอบของคุณคล้ายกับคำตอบนี้จาก @Arpad มันใช้งานได้สำหรับฉัน ใช้ Spring Security 3.2.9 stackoverflow.com/a/8426947/4505142
Darren Parker

11

ฉันแก้ไขได้โดยใส่สิ่งต่อไปนี้ในหน้า login.php ของฉัน

<script type="text/javascript">
    if (top.location.href.indexOf('login.php') == -1) {
        top.location.href = '/login.php';
    }
</script>

10

ให้ฉันพูดปัญหาอีกครั้งตามที่อธิบายโดย @Steg

ฉันมีปัญหาคล้ายกับของคุณ ฉันทำการร้องขอ ajax ที่มี 2 คำตอบที่เป็นไปได้: อันที่เปลี่ยนเส้นทางเบราว์เซอร์ไปยังหน้าใหม่และอีกอันที่แทนที่รูปแบบ HTML ที่มีอยู่ในหน้าปัจจุบันด้วยหน้าใหม่

IMHO นี่เป็นความท้าทายที่แท้จริงและจะต้องมีการขยายอย่างเป็นทางการในมาตรฐาน HTTP ปัจจุบัน

ฉันเชื่อว่า Http Standard ใหม่จะใช้รหัสสถานะใหม่ ความหมาย: ขณะนี้301/302บอกเบราว์เซอร์ที่จะไปและดึงเนื้อหาของคำขอนี้เพื่อใหม่locationคำขอไปอยู่ที่ใหม่

ในมาตรฐานแบบขยายจะกล่าวได้ว่าหากการตอบสนองstatus: 308(เป็นเพียงตัวอย่าง) เบราว์เซอร์ควรเปลี่ยนเส้นทางไปยังหน้าหลักlocationระบุไว้

ที่ถูกกล่าว; ฉันมีแนวโน้มที่จะเลียนแบบพฤติกรรมในอนาคตนี้แล้วดังนั้นเมื่อจำเป็นต้องใช้ document.redirect ฉันมีเซิร์ฟเวอร์ที่ตอบสนองเป็น:

status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html

เมื่อ JS ได้รับ " status: 204" มันจะตรวจสอบการมีอยู่ของx-status: 308ส่วนหัวและทำ document.redirect ไปยังหน้าที่ให้ไว้ในlocationส่วนหัว

สิ่งนี้มีเหตุผลสำหรับคุณหรือไม่?


7

บางคนอาจพบว่ามีประโยชน์ด้านล่าง:

ฉันต้องการให้ลูกค้าเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบสำหรับการกระทำใด ๆ ที่ส่งโดยไม่มีโทเค็นการให้สิทธิ์ เนื่องจากการกระทำทั้งหมดของฉันเป็นไปตามอาแจ็กซ์ฉันจึงต้องมีวิธีทั่วไปที่ดีในการเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบแทนการจัดการฟังก์ชั่นความสำเร็จของอาแจ็กซ์

นี่คือสิ่งที่ฉันทำ:

ในคำขอ Ajax ใด ๆ เซิร์ฟเวอร์ของฉันจะส่งคืนคำตอบ Json 200 "ต้องเป็นผู้มีอำนาจ" (หากลูกค้าต้องการตรวจสอบสิทธิ์)

ตัวอย่างง่ายๆใน Java (ฝั่งเซิร์ฟเวอร์):

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);

    public static final String COOKIE_NAME = "token_cookie"; 

    @Override
    public void filter(ContainerRequestContext context) throws IOException {        
        // Check if it has a cookie.
        try {
            Map<String, Cookie> cookies = context.getCookies();

            if (!cookies.containsKey(COOKIE_NAME)) {
                m_logger.debug("No cookie set - redirect to login page");
                throw new AuthenticationException();
            }
        }
        catch (AuthenticationException e) {
            context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
        }
    }
}

ใน Javascript ของฉันฉันได้เพิ่มรหัสต่อไปนี้:

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    var originalSuccess = options.success;

    options.success = function(data) {
        if (data == "NEED TO AUTHENTICATE") {
            window.location.replace("/login.html");
        }
        else {
            originalSuccess(data);
        }
    };      
});

และนั่นคือเกี่ยวกับมัน


5

ใน servlet คุณควร response.setStatus(response.SC_MOVED_PERMANENTLY); ส่งสถานะ '301' xmlHttp ที่คุณต้องการสำหรับการเปลี่ยนเส้นทาง ...

และในฟังก์ชัน $ .ajax คุณไม่ควรใช้.toString()ฟังก์ชัน ... เพียง

if (xmlHttp.status == 301) { top.location.href = 'xxxx.jsp'; }

ปัญหาคือมันไม่ยืดหยุ่นมากคุณไม่สามารถตัดสินใจได้ว่าคุณต้องการเปลี่ยนเส้นทางที่ใด ..

การเปลี่ยนเส้นทางผ่านเซิร์ฟเล็ตควรเป็นวิธีที่ดีที่สุด แต่ฉันก็ยังหาวิธีที่ถูกต้องไม่ได้


5

ฉันแค่อยากจะดึงคำขอ ajax ใด ๆ สำหรับทั้งหน้า @ SuperG ให้ฉันเริ่มต้น นี่คือสิ่งที่ฉันลงเอยด้วย:

// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
        switch(request.status) {
            case 301: case 404: case 403:                    
                window.location.replace("http://mysite.tld/login");
                break;
        }
});

ฉันต้องการตรวจสอบรหัสสถานะ http บางอย่างเพื่อระบุการตัดสินใจของฉัน อย่างไรก็ตามคุณสามารถผูกไว้กับ ajaxError เพื่อรับสิ่งอื่นนอกเหนือจากความสำเร็จ (บางทีอาจเป็นแค่ 200 เท่านั้น) ฉันอาจจะเขียน:

$('body').bind('ajaxError', function(event,request,settings){
    window.location.replace("http://mysite.tld/login");
}

1
หลังจะซ่อนข้อผิดพลาดอื่น ๆ ที่ทำให้เกิดปัญหาในการแก้ไขปัญหา
Tim Abell

1
403 ไม่ได้หมายความว่าผู้ใช้ไม่ได้รับการรับรองความถูกต้องซึ่งหมายความว่าผู้ใช้ (อาจรับรองความถูกต้อง) ไม่ได้รับอนุญาตให้ดูทรัพยากรที่ร้องขอ ดังนั้นจึงไม่ควรเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ
Rob

5

หากคุณต้องการส่งผ่านค่าจากนั้นคุณยังสามารถตั้งค่าตัวแปรเซสชันและการเข้าถึงเช่น: ใน jsp ของคุณคุณสามารถเขียน

<% HttpSession ses = request.getSession(true);
   String temp=request.getAttribute("what_you_defined"); %>

จากนั้นคุณสามารถเก็บค่าอุณหภูมินี้ไว้ในตัวแปรจาวาสคริปต์ของคุณและเล่นไปเรื่อย ๆ


5

ฉันไม่ประสบความสำเร็จกับโซลูชันส่วนหัว - พวกเขาไม่เคยเลือกวิธี ajaxSuccess / ajaxComplete ของฉัน ฉันใช้คำตอบของ Steg กับการตอบกลับที่กำหนดเอง แต่ฉันแก้ไขบางส่วนของ JS ฉันตั้งค่าวิธีการที่ฉันเรียกใช้ในแต่ละฟังก์ชั่นเพื่อให้ฉันสามารถใช้มาตรฐาน$.getและ$.postวิธีการได้

function handleAjaxResponse(data, callback) {
    //Try to convert and parse object
    try {
        if (jQuery.type(data) === "string") {
            data = jQuery.parseJSON(data);
        }
        if (data.error) {
            if (data.error == 'login') {
                window.location.reload();
                return;
            }
            else if (data.error.length > 0) {
                alert(data.error);
                return;
            }
        }
    }
    catch(ex) { }

    if (callback) {
        callback(data);
    }
}

ตัวอย่างของการใช้งาน ...

function submitAjaxForm(form, url, action) {
    //Lock form
    form.find('.ajax-submit').hide();
    form.find('.loader').show();

    $.post(url, form.serialize(), function (d) {
        //Unlock form
        form.find('.ajax-submit').show();
        form.find('.loader').hide();

        handleAjaxResponse(d, function (data) {
            // ... more code for if auth passes ...
        });
    });
    return false;
}

5

HTTP Headerสุดท้ายผมแก้ปัญหาโดยการเพิ่มที่กำหนดเอง ก่อนการตอบสนองสำหรับทุกคำขอในฝั่งเซิร์ฟเวอร์ฉันเพิ่ม URL ที่ร้องขอปัจจุบันไปยังส่วนหัวของการตอบกลับ

แอปพลิเคชันของฉันบนเซิร์ฟเวอร์คือAsp.Net MVCและมีสถานที่ที่น่าสนใจ ในGlobal.asaxฉันใช้งานApplication_EndRequestเหตุการณ์ดังนั้น:

    public class MvcApplication : System.Web.HttpApplication
    {

    //  ...
    //  ...

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
        }

    }

มันใช้งานได้สมบูรณ์แบบสำหรับฉัน! ตอนนี้ในการตอบสนองของทุกJQuery $.postฉันได้รับการร้องขอurlและส่วนหัวของการตอบสนองอื่น ๆ ที่มาเป็นผลมาจากPOSTวิธีการตามสถานะ302,303 ...

และสิ่งสำคัญอื่น ๆ คือไม่จำเป็นต้องแก้ไขโค้ดในฝั่งเซิร์ฟเวอร์หรือฝั่งไคลเอ็นต์

และต่อไปคือความสามารถในการเข้าถึงข้อมูลอื่น ๆ ของการโพสต์การกระทำข้อผิดพลาดข้อความและ ... ในวิธีนี้

ฉันโพสต์สิ่งนี้อาจช่วยใครบางคน :)


4

ฉันพบปัญหานี้ในแอพ django ที่ฉันกำลังแก้ไข (ข้อจำกัดความรับผิดชอบ: ฉันกำลังจะเรียนรู้และฉันไม่ชำนาญ) สิ่งที่ฉันต้องการทำคือใช้ jQuery ajax เพื่อส่งคำขอ DELETE ไปยังทรัพยากรลบมันบนฝั่งเซิร์ฟเวอร์จากนั้นส่งการเปลี่ยนเส้นทางกลับไปที่ (โดยทั่วไป) โฮมเพจ เมื่อฉันส่งHttpResponseRedirect('/the-redirect/')จากสคริปต์ python วิธี ajax ของ jQuery ได้รับ 200 แทน 302 ดังนั้นสิ่งที่ฉันทำคือส่งการตอบกลับ 300 ด้วย:

response = HttpResponse(status='300')
response['Location'] = '/the-redirect/' 
return  response

จากนั้นฉันก็ส่ง / จัดการคำขอบนไคลเอนต์ด้วย jQuery.ajax ดังนี้:

<button onclick="*the-jquery*">Delete</button>

where *the-jquery* =
$.ajax({ 
  type: 'DELETE', 
  url: '/resource-url/', 
  complete: function(jqxhr){ 
    window.location = jqxhr.getResponseHeader('Location'); 
  } 
});

บางทีการใช้ 300 อาจไม่ถูกต้อง แต่อย่างน้อยก็ใช้ได้เหมือนที่ฉันต้องการ

PS: นี่เป็นความเจ็บปวดอย่างมากในการแก้ไข SO เวอร์ชันมือถือ ISP ที่โง่ทำให้คำขอการยกเลิกบริการของฉันถูกต้องเมื่อฉันตอบคำถามเสร็จแล้ว!


4

คุณยังสามารถขอ XMLHttpRequest ส่งต้นแบบ สิ่งนี้จะใช้ได้กับการส่งทั้งหมด (jQuery / dojo / etc) ด้วยตัวจัดการเดียว

ฉันเขียนโค้ดนี้เพื่อจัดการข้อผิดพลาดที่หมดอายุ 500 หน้า แต่มันควรจะทำงานเช่นกันเพื่อดักจับการเปลี่ยนเส้นทาง 200 พร้อมรายการ wikipedia บนXMLHttpRequest onreadystatechange เกี่ยวกับความหมายของ readyState

// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.send = function() {
  //console.dir( this );

  this.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
      try {
        document.documentElement.innerHTML = this.responseText;
      } catch(error) {
        // IE makes document.documentElement read only
        document.body.innerHTML = this.responseText;
      }
    }
  };

  oldXMLHttpRequestSend.apply(this, arguments);
}

2

นอกจากนี้คุณอาจต้องการเปลี่ยนเส้นทางผู้ใช้ไปยัง URL ที่กำหนดในส่วนหัว ดังนั้นในที่สุดมันจะเป็นดังนี้:

$.ajax({
    //.... other definition
    complete:function(xmlHttp){
        if(xmlHttp.status.toString()[0]=='3'){
        top.location.href = xmlHttp.getResponseHeader('Location');
    }
});

UPD: Opps มีงานเดียวกัน แต่ไม่ได้ผล ทำสิ่งนี้ ฉันจะแสดงวิธีแก้ปัญหาให้คุณเมื่อฉันพบมัน


4
คำตอบนี้ควรถูกลบโดยผู้เขียนในขณะที่ตัวเขาเองบอกว่ามันไม่ทำงาน เขาสามารถโพสต์วิธีแก้ปัญหาการทำงานในภายหลัง -1
TheCrazyProgrammer

แนวคิดดี แต่ปัญหาคือส่วนหัว "ตำแหน่ง" ไม่เคยผ่าน
Martin Zvarík

การแก้ไขของฉันถูกปฏิเสธดังนั้น ... คุณต้องใช้ "var xmlHttp = $ .ajax ({.... ") ตัวแปรไม่สามารถอยู่ข้างใน ... จากนั้นใช้: console.log (xmlHttp.getAllResponseHeaders ()) ;
Martin Zvarík

2

ฉันได้รับการทำงานโดยใช้คำตอบจาก ลิงค์ @John และ @Arpad และ @RobWinchลิงก์

ฉันใช้ Spring Security 3.2.9 และ jQuery 1.10.2

ขยายคลาสของ Spring เพื่อให้เกิดการตอบสนอง 4XX จากคำขอ AJAX เท่านั้น:

public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {

    public CustomLoginUrlAuthenticationEntryPoint(final String loginFormUrl) {
        super(loginFormUrl);
    }

    // For AJAX requests for user that isn't logged in, need to return 403 status.
    // For normal requests, Spring does a (302) redirect to login.jsp which the browser handles normally.
    @Override
    public void commence(final HttpServletRequest request,
                         final HttpServletResponse response,
                         final AuthenticationException authException)
            throws IOException, ServletException {
        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
        } else {
            super.commence(request, response, authException);
        }
    }
}

ApplicationContext-security.xml

  <security:http auto-config="false" use-expressions="true" entry-point-ref="customAuthEntryPoint" >
    <security:form-login login-page='/login.jsp' default-target-url='/index.jsp'                             
                         authentication-failure-url="/login.jsp?error=true"
                         />    
    <security:access-denied-handler error-page="/errorPage.jsp"/> 
    <security:logout logout-success-url="/login.jsp?logout" />
...
    <bean id="customAuthEntryPoint" class="com.myapp.utils.CustomLoginUrlAuthenticationEntryPoint" scope="singleton">
        <constructor-arg value="/login.jsp" />
    </bean>
...
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
    <property name="requestMatcher">
      <bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
        <constructor-arg>
          <bean class="org.springframework.security.web.util.matcher.MediaTypeRequestMatcher">
            <constructor-arg>
              <bean class="org.springframework.web.accept.HeaderContentNegotiationStrategy"/>
            </constructor-arg>
            <constructor-arg value="#{T(org.springframework.http.MediaType).APPLICATION_JSON}"/>
            <property name="useEquals" value="true"/>
          </bean>
        </constructor-arg>
      </bean>
    </property>
</bean>

ใน JSP ของฉันเพิ่มตัวจัดการข้อผิดพลาด AJAX ทั่วโลกตามที่แสดงไว้ที่นี่

  $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
      if ( jqxhr.status === 403 ) {
          window.location = "login.jsp";
      } else {
          if(thrownError != null) {
              alert(thrownError);
          } else {
              alert("error");
          }
      }
  });

และลบตัวจัดการข้อผิดพลาดที่มีอยู่ออกจากการโทร AJAX ในหน้า JSP:

        var str = $("#viewForm").serialize();
        $.ajax({
            url: "get_mongoDB_doc_versions.do",
            type: "post",
            data: str,
            cache: false,
            async: false,
            dataType: "json",
            success: function(data) { ... },
//            error: function (jqXHR, textStatus, errorStr) {
//                 if(textStatus != null)
//                     alert(textStatus);
//                 else if(errorStr != null)
//                     alert(errorStr);
//                 else
//                     alert("error");
//            }
        });

ฉันหวังว่ามันจะช่วยผู้อื่น

Update1 ฉันพบว่าฉันจำเป็นต้องเพิ่มตัวเลือก (always-use-default-target = "true") ในการกำหนดค่าแบบฟอร์มการเข้าสู่ระบบ สิ่งนี้จำเป็นเนื่องจากหลังจากที่คำขอ AJAX ถูกเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ (เนื่องจากเซสชันหมดอายุ) Spring จะจดจำคำขอ AJAX ก่อนหน้าและเปลี่ยนเส้นทางอัตโนมัติไปยังหน้าหลังจากเข้าสู่ระบบ สิ่งนี้ทำให้ JSON ที่ส่งคืนถูกแสดงบนหน้าเบราว์เซอร์ แน่นอนไม่ใช่สิ่งที่ฉันต้องการ

Update2 แทนที่จะใช้always-use-default-target="true"ให้ใช้ @RobWinch ตัวอย่างของการบล็อกคำขอ AJAX จาก requstCache วิธีนี้ช่วยให้ลิงก์ปกติสามารถเปลี่ยนเส้นทางไปยังเป้าหมายดั้งเดิมของพวกเขาหลังจากเข้าสู่ระบบ แต่ AJAX ไปที่หน้าแรกหลังจากเข้าสู่ระบบ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.