Response.End () ถือว่าเป็นอันตรายหรือไม่


198

บทความ KB นี้บอกว่า ASP.NET Response.End()ยกเลิกการใช้เธรด

Reflector แสดงให้เห็นว่ามันเป็นเช่นนี้:

public void End()
{
    if (this._context.IsInCancellablePeriod)
    {
        InternalSecurityPermissions.ControlThread.Assert();
        Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
    }
    else if (!this._flushing)
    {
        this.Flush();
        this._ended = true;
        if (this._context.ApplicationInstance != null)
        {
            this._context.ApplicationInstance.CompleteRequest();
        }
    }
}

มันช่างโหดร้ายเหลือเกินสำหรับฉัน ดังที่บทความ KB กล่าวว่าโค้ดใด ๆ ในแอปพลิเคชันต่อไปนี้Response.End()จะไม่ถูกประมวลผลและที่ละเมิดหลักการของความประหลาดใจอย่างน้อยที่สุด มันเกือบจะเหมือนApplication.Exit()ในแอพ WinForms ข้อยกเว้นการยกเลิกเธรดที่เกิดจากResponse.End()ไม่สามารถจับได้ดังนั้นการล้อมรอบโค้ดในtry... finallyจะไม่เป็นไปตาม

Response.End()มันทำให้ฉันสงสัยว่าฉันควรหลีกเลี่ยง

ใครสามารถแนะนำฉันควรใช้Response.End()เมื่อใดResponse.Close()และเมื่อไหร่HttpContext.Current.ApplicationInstance.CompleteRequest()?

Ref: รายการบล็อกริกราล์ของ


จากคำตอบที่ฉันได้รับคำตอบของฉันคือใช่Response.Endเป็นอันตรายแต่มีประโยชน์ในบางกรณี

  • ใช้Response.End()เป็นการโยนที่ไม่สามารถจับตาดูได้เพื่อยุติHttpResponseเงื่อนไขพิเศษทันที จะมีประโยชน์ในระหว่างการดีบักด้วย หลีกเลี่ยงResponse.End()การตอบประจำสมบูรณ์
  • ใช้Response.Close()เพื่อปิดการเชื่อมต่อกับไคลเอนต์ทันที สำหรับการโพสต์บล็อก MSDNนี้วิธีนี้ไม่ได้มีไว้สำหรับการประมวลผลคำขอ HTTP ปกติ ไม่น่าเป็นไปได้สูงที่คุณจะมีเหตุผลที่ดีที่จะเรียกวิธีนี้
  • ใช้CompleteRequest()เพื่อยุติการคำขอปกติ CompleteRequestทำให้ไปป์ไลน์ ASP.NET เพื่อข้ามไปข้างหน้ากับEndRequestเหตุการณ์หลังจากHttpApplicationเหตุการณ์ปัจจุบันเสร็จสมบูรณ์ ดังนั้นถ้าคุณโทรCompleteRequestแล้วเขียนอะไรมากกว่านี้เพื่อตอบสนองการเขียนจะถูกส่งไปยังลูกค้า

แก้ไข - 13 เมษายน 2554

ความชัดเจนเพิ่มเติมมีอยู่ที่นี่:
- โพสต์ที่เป็นประโยชน์ในบล็อก MSDN
- การวิเคราะห์ที่เป็นประโยชน์โดย Jon Reid


2
ไม่มีความคิดอะไรที่เปลี่ยนแปลงไปตั้งแต่คำตอบนี้ แต่ฉันResponse.End ThreadAbortExceptionก็เข้าใจได้ดี
Maslow

1
จำไว้ว่าResponse.RedirectและServer.Transferเรียกร้องทั้งสองResponse.Endและควรหลีกเลี่ยง
Owen Blacker

คำตอบ:


66

หากคุณใช้งานตัวบันทึกข้อยกเว้นในแอปของคุณแอปดังกล่าวจะถูกให้น้ำThreadAbortExceptionจากการResponse.End()โทรที่ไม่เป็นอันตรายเหล่านี้ ฉันคิดว่านี่เป็นวิธีของ Microsoft ในการพูดว่า "เคาะออก!"

ฉันจะใช้Response.End()ก็ต่อเมื่อมีเงื่อนไขพิเศษบางอย่างและไม่สามารถดำเนินการอื่นได้ บางทีการบันทึกข้อยกเว้นนี้อาจบ่งบอกถึงคำเตือน


107

TL; DR

เริ่มแรกฉันแนะนำว่าคุณควรแทนที่การเรียกทั้งหมดของคุณเป็น [Response.End] ด้วย [... ] การโทร CompleteRequest () แต่ถ้าคุณต้องการหลีกเลี่ยงการประมวลผลหลังการโพสต์และการแสดง html คุณจะต้องเพิ่ม [.. .] แทนที่ด้วย

Jon Reid "การวิเคราะห์ขั้นสุดท้าย"


ต่อ MSDN, Jon Reidและ Alain Renon:

ประสิทธิภาพ ASP.NET - การจัดการข้อยกเว้น - เขียนโค้ดที่หลีกเลี่ยงข้อยกเว้น

เมธอด Server.Transfer, Response.Redirect, Response.End ยกข้อยกเว้นทั้งหมด แต่ละวิธีการเหล่านี้เรียกการตอบสนองภายใน เรียกร้องให้ Response.End ในที่สุดก็ทำให้เกิดข้อยกเว้น ThreadAbortException

โซลูชั่น ThreadAbortException

HttpApplication.CompleteRequest () ตั้งค่าตัวแปรที่ทำให้เธรดข้ามเหตุการณ์ส่วนใหญ่ในขั้นตอนเหตุการณ์ HttpApplication ที่ผ่านมา [-] ไม่ใช่ห่วงโซ่เหตุการณ์ของหน้า แต่เป็นการเชื่อมโยงเหตุการณ์ของแอปพลิเคชัน

...

สร้างตัวแปรระดับชั้นที่ตั้งค่าสถานะว่าหน้าควรจะยุติแล้วตรวจสอบตัวแปรก่อนที่จะประมวลผลเหตุการณ์ของคุณหรือการแสดงผลหน้าของคุณ [... ] ฉันอยากจะแนะนำเพียงเอาชนะวิธี RaisePostBackEvent และ Render

Response.End และ Response.Close จะไม่ใช้ในการประมวลผลคำขอปกติเมื่อประสิทธิภาพเป็นสิ่งสำคัญ Response.End เป็นวิธีที่สะดวกและหนักหน่วงในการยกเลิกการประมวลผลคำขอที่มีการปรับประสิทธิภาพที่เกี่ยวข้อง Response.Close สำหรับการยุติการตอบสนอง HTTP ที่ระดับ IIS / socket ในทันทีและทำให้เกิดปัญหากับสิ่งต่าง ๆ เช่น KeepAlive

วิธีที่แนะนำในการจบการร้องขอ ASP.NET คือ HttpApplication.CompleteRequest โปรดจำไว้ว่าการเรนเดอร์ ASP.NET จะต้องถูกข้ามไปด้วยตนเองตั้งแต่ HttpApplication.CompleteRequest ข้ามส่วนที่เหลือของไพพ์ไลน์แอปพลิเคชัน IIS / ASP.NET ไม่ใช่ไปป์ไลน์เพจ ASP.NET (ซึ่งเป็นขั้นตอนเดียวในไพพ์ไลน์แอป)


รหัส

ลิขสิทธิ์© 2001-2007, C6 Software, Incที่สุดเท่าที่ฉันจะบอกได้


การอ้างอิง

HttpApplication.CompleteRequest

สาเหตุ ASP.NET จะข้ามเหตุการณ์และการกรองทั้งหมดใน HTTP Pipeline ของการดำเนินการและดำเนินการโดยตรงกับเหตุการณ์ EndRequest

Response.End

วิธีนี้มีไว้สำหรับความเข้ากันได้กับ ASP เท่านั้น - นั่นคือเพื่อความเข้ากันได้กับเทคโนโลยีการเขียนโปรแกรมบนเว็บที่ใช้ COM ซึ่งนำหน้า ASP.NET.preceded ASP.NET [เน้นเพิ่ม]

Response.Close

วิธีการนี้จะยุติการเชื่อมต่อไปยังลูกค้าในลักษณะอย่างกระทันหันและเป็นที่ ไม่ได้มีไว้สำหรับการประมวลผลการร้องขอ HTTP ปกติ [เน้นเพิ่ม]


4
> โปรดทราบว่าการเรนเดอร์ ASP.NET จะต้องถูกข้ามด้วยตนเองตั้งแต่ HttpApplication.CompleteRequest ข้ามส่วนที่เหลือของไพพ์ไลน์แอปพลิเคชัน IIS / ASP.NET ไม่ใช่ไปป์ไลน์เพจ ASP.NET (ซึ่งเป็นขั้นตอนเดียวในไพพ์ไลน์แอป) และคุณจะทำสิ่งนี้ให้สำเร็จได้อย่างไร?
PilotBob

1
ดูลิงก์ไปยังโค้ดที่ Jon Reid สาธิตวิธีตั้งค่าสถานะและแทนที่เมธอด RaisePostBackEvent และ Render ของเพจเพื่อข้ามการใช้งานปกติเมื่อต้องการ (คุณอาจทำสิ่งนี้ในคลาสฐานหน้าของแอปทั้งหมดของคุณควรสืบทอดมา) web.archive.org/web/20101224113858/http://www.c6software.com/?hl=th
user423430

4
เพียงเพื่อย้ำ: HttpApplication.CompleteRequest ไม่ได้ยุติการตอบสนองเช่น Response.End
เกลน Little

2
HttpApplication.CompleteRequest ยังไม่หยุดการไหลของโค้ดดังนั้นบรรทัดที่ตามมาจะยังคงทำงานต่อไป นั่นอาจไม่ส่งผลกระทบต่อสิ่งที่เบราว์เซอร์เห็น แต่หากบรรทัดเหล่านั้นทำการประมวลผลอื่น ๆ อาจทำให้เกิดความสับสนได้
Joshua Frank

3
ฉันไม่สามารถคิดได้ แต่แบบฟอร์มบนเว็บนั้นใช้การไม่ได้ ประสิทธิภาพที่ลดลงมากขึ้นคืออะไรการเรียก Response.End () หรือให้หน้าโหลดทุกอย่างแล้วหยุดการตอบสนอง ฉันไม่สามารถดูได้ว่าการตอบสนองจากที่ใด () เป็น "มากกว่า" ที่เป็นอันตรายที่นี่ ยิ่งไปกว่านั้น Microsoft ถือว่า `ThreadAbortedException 'เป็นเหตุการณ์ปกติซึ่งเห็นได้จากรหัสนี้: Referencesource.microsoft.com/#System.Web/UI/Page.cs,4875สิ่งหนึ่งที่ขัดต่อ Response.End () คือสิ่งนั้นอาจล้มเหลว ยกเลิกการตอบสนองซึ่งอาจส่งผลให้เกิดการตอบสนองเป็นครั้งคราว
Ghasan

99

คำถามนี้ปรากฏขึ้นที่ด้านบนสุดของ google ค้นหาข้อมูลเกี่ยวกับการตอบกลับดังนั้นสำหรับการค้นหาอื่น ๆ เช่นตัวฉันเองที่ต้องการโพสต์ CSV / XML / PDF ฯลฯ เพื่อตอบสนองต่อเหตุการณ์โดยไม่ต้องแสดงหน้า ASPX ทั้งหมดนี่คือวิธีที่ฉันทำ . (การแทนที่เมธอดการเรนเดอร์นั้นซับซ้อนเกินไปสำหรับ IMO แบบง่ายๆ)

// Add headers for a csv file or whatever
Response.ContentType = "text/csv"
Response.AddHeader("Content-Disposition", "attachment;filename=report.csv")
Response.AddHeader("Pragma", "no-cache")
Response.AddHeader("Cache-Control", "no-cache")

// Write the data as binary from a unicode string
Dim buffer As Byte()
buffer = System.Text.Encoding.Unicode.GetBytes(csv)
Response.BinaryWrite(buffer)

// Sends the response buffer
Response.Flush()

// Prevents any other content from being sent to the browser
Response.SuppressContent = True

// Directs the thread to finish, bypassing additional processing
HttpContext.Current.ApplicationInstance.CompleteRequest()

1
คุณไม่ควรใช้เพจ APSX เพื่อทำสิ่งนี้ มันเป็นความพยายามที่สูญเปล่าไปมาก คุณควรจะใช้ ASMX หรือ Web Service ไม่ว่าจะเป็นหน้า ASPX
mattmanser

19
นี่น่าจะเป็นคำตอบที่ง่ายที่สุด กุญแจสำคัญคือ Response.SuppressContent = True
Chris Weber

3
@mattmanser - ไม่ใช่เรื่องง่าย / ดีที่สุด / แนะนำให้เลือกที่จะมีหน้าแยกสำหรับการแสดงที่แตกต่างกันของทรัพยากรเดียวกัน ลองนึกถึง REST เป็นต้นหากลูกค้าระบุว่าพวกเขาต้องการ csv, xml ผ่านส่วนหัวหรือพารามิเตอร์วิธีนี้น่าจะดีที่สุดในขณะที่ยังคงให้การสนับสนุน html ผ่านทางเครื่องมือการแสดงผลปกติของ asp.net
Chris Weber

1
สิ่งนี้ไม่ได้ผลสำหรับฉัน ฉันมีหน้าเว็บที่ทำงานร่วมกับ Response.End () แต่ใช้การรวมกันทุกประเภทของ Response.Close (), Response.Flush () HttpContext.Current.ApplicationInstance.CompleteRequest () และสิ่งอื่น ๆ ไม่ทำงานถ้าฉันมีตัวกรอง GzipStream ในการตอบสนอง สิ่งที่ดูเหมือนจะเกิดขึ้นคือหน้านั้นยังคงมีการส่งออกพร้อมกับไฟล์ของฉัน ในที่สุดฉันก็เลยใช้ฟังก์ชัน Render () เพื่อว่างเปล่า
Aerik

1
CompleteRequest ข้ามส่วนต่างๆของไพพ์ไลน์ของแอปพลิเคชั่น แต่จะยังคงทำงานตลอดเวลาที่เหลือของกระบวนการเรนเดอร์เพจซึ่งจะไม่หยุดทันทีเช่นการตอบกลับ มีการอธิบายเชิงลึกมากขึ้นว่าทำไมในคำตอบอื่น ๆ ในหน้านี้
Jay Zelos

11

ในคำถามของ "ฉันยังไม่ทราบความแตกต่างระหว่าง Response.Close และ CompleteRequest ()" ฉันจะบอกว่า:

ต้องการใช้ CompleteRequest () อย่าใช้ Response.Close ()

ดูบทความต่อไปนี้สำหรับการสรุปที่สมบูรณ์ของกรณีนี้

โปรดทราบว่าแม้หลังจากเรียก CompleteRequest () ข้อความบางส่วน (เช่น redndered จากรหัส ASPX) จะถูกผนวกเข้ากับสตรีมเอาต์พุตการตอบกลับ คุณสามารถป้องกันได้โดยการเอาชนะ Render และวิธีการ RaisePostBackEvent ที่อธิบายไว้ในบทความต่อไปนี้

BTW: ฉันเห็นด้วยกับการป้องกันการใช้ Response.End () โดยเฉพาะอย่างยิ่งเมื่อเขียนข้อมูลไปยังสตรีม http เพื่อจำลองการดาวน์โหลดไฟล์ เราได้ใช้ Response.End () ในอดีตจนกระทั่งล็อกไฟล์ของเราเต็มไปด้วย ThreadAbortExceptions


ฉันสนใจที่จะลบล้าง Render ตามที่คุณอธิบาย แต่ลิงก์ไปยัง "บทความต่อไปนี้" นั้นตายแล้ว บางทีคุณสามารถอัปเดตรายการของคุณ?
paqogomez

ขออภัยสำหรับคำตอบที่ล่าช้า ฉันจำไม่ได้ว่ามีอะไรอยู่ในบทความนั้น อย่างไรก็ตามฉันได้พบแล้วใน webarchive.org: web.archive.org/web/20101224113858/http://www.c6software.com/…
ม.ค. Šotola

9

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


คำแนะนำ 2 เปอร์เซ็นต์ของฉัน
หลีกเลี่ยงโดยใช้Response.End()เป็นโฟลว์ควบคุม
DOใช้Response.End()ถ้าคุณจำเป็นต้องหยุดการดำเนินการขอและทราบว่า (ปกติ) * รหัสจะไม่มีการดำเนินการที่ผ่านมาจุดนั้น


* Response.End()และThreadAbortException s

Response.End() พ่น ThreadAbortException เป็นส่วนหนึ่งของการใช้งานปัจจุบัน (ดังที่ระบุไว้โดย OP)

ThreadAbortException เป็นข้อยกเว้นพิเศษที่สามารถถูกดักจับได้ แต่มันจะถูกยกขึ้นอีกครั้งโดยอัตโนมัติเมื่อสิ้นสุดการบล็อก catch

เพื่อดูวิธีการเขียนโค้ดที่ต้องจัดการกับ ThreadAbortExceptions ดู @ ตอบ Mehrdad ไปดังนั้นฉันสามารถตรวจสอบ ThreadAbortException ในที่สุดก็ปิดกั้นที่เขาอ้างอิงวิธี RuntimeHelpers.ExecuteCodeWithGuaranteedCleanupและข้อ จำกัด การดำเนินการภูมิภาค


บทความริกราล์กล่าวถึงเป็นบทเรียนและให้แน่ใจว่าจะอ่านความคิดเห็นได้เป็นอย่างดี โปรดทราบว่าปัญหาของ Strahl นั้นเฉพาะเจาะจง เขาต้องการรับข้อมูลไปยังไคลเอนต์ (รูปภาพ) จากนั้นประมวลผลการอัพเดตฐานข้อมูลการติดตามที่ไม่ทำให้การแสดงรูปภาพช้าลงซึ่งทำให้เขามีปัญหาในการทำบางสิ่งบางอย่างหลังจาก Response.End ถูกเรียกใช้


เราเห็นโพสต์นี้stackoverflow.com/questions/16731745/…แนะนำให้ใช้ Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () แทน Response.End ()
jazzBox

3

ฉันไม่เคยพิจารณาการใช้ Response.End () เพื่อควบคุมการไหลของโปรแกรม

อย่างไรก็ตาม Response.End () อาจมีประโยชน์ตัวอย่างเช่นเมื่อให้บริการไฟล์แก่ผู้ใช้

คุณเขียนไฟล์ลงในการตอบกลับและคุณไม่ต้องการให้มีสิ่งใดเพิ่มเข้าไปในการตอบกลับเนื่องจากไฟล์ของคุณอาจเสียหาย


1
ฉันเข้าใจความต้องการ API ที่จะพูดว่า "การตอบสนองเสร็จสมบูรณ์" แต่ Response.End () ก็ทำเช่นเดียวกัน นี่คือปมของคำถาม เมื่อใดที่ควรจับคู่สองสิ่งนี้
Cheeso

2

ฉันใช้ Response.End () ทั้งใน. NET และ Classic ASP สำหรับการสิ้นสุดสิ่งต่างๆก่อนหน้านี้ ตัวอย่างเช่นฉันใช้เมื่อมีจำนวนครั้งของการพยายามเข้าสู่ระบบรับรอง หรือเมื่อหน้าเว็บที่ปลอดภัยถูก accesed จากการเข้าสู่ระบบไม่ได้ตรวจสอบ (ตัวอย่างคร่าวๆ):

    if (userName == "")
    {
        Response.Redirect("......");
        Response.End();
    }
    else
    {
      .....

เมื่อให้บริการไฟล์แก่ผู้ใช้ที่ฉันต้องการใช้ Flush จุดจบอาจทำให้เกิดปัญหาได้


โปรดจำไว้ว่า Flush () ไม่ใช่ "นี่คือจุดจบ" มันเป็นแค่ "ชะล้างทุกอย่างจนบัดนี้" เหตุผลที่คุณอาจต้องการ "นี่คือจุดจบ" คือเพื่อให้ลูกค้าทราบว่ามีเนื้อหาทั้งหมดในขณะที่เซิร์ฟเวอร์สามารถไปและทำสิ่งอื่น ๆ - อัปเดตล็อกไฟล์สอบถามตัวนับฐานข้อมูลหรืออะไรก็ตาม ถ้าคุณเรียกใช้ Response.Flush จากนั้นทำอย่างใดอย่างหนึ่งต่อไปนี้ไคลเอนต์อาจรอต่อไปอีก ถ้าคุณเรียก Response.End () แล้วกระโดดออกจากการควบคุมและ DB ไม่ได้รับแบบสอบถาม ฯลฯ
Cheeso

คุณสามารถใช้ override Response.Redirect (".... ", true) แทนโดยที่บูลคือ 'endResponse: ระบุว่าการดำเนินการปัจจุบันของหน้าเว็บควรจะสิ้นสุดลง "
Robert Paulson

การใช้เฟรมเวิร์กการรับรองความถูกต้องแบบฟอร์มดีกว่าเสมอเพื่อปกป้องเพจที่มีความปลอดภัยโดยใช้ข้อมูลรับรองการเข้าสู่ระบบ
Robert Paulson

3
ที่จริงเพื่อแก้ไขตัวเองฉันเชื่อว่าการเริ่มต้นของ Response.Redirect และ Server.Transfer จะเรียกการตอบสนองภายในและภายในถ้าคุณเรียกการแทนที่และผ่าน 'เท็จ' ในวิธีการเขียนรหัสของคุณการตอบสนองและจะไม่เรียกว่า ,
Robert Paulson

1
Response.end ทำงานแตกต่างกันมากใน. net มากกว่าใน ASP แบบคลาสสิก ใน. net มันเป็นสาเหตุของการทำ threadabortexception ซึ่งค่อนข้างน่ารังเกียจ
Andy

2

ฉันใช้ Response.End () เป็นเครื่องมือทดสอบ / ตรวจแก้จุดบกพร่องเท่านั้น

<snip>
Response.Write("myVariable: " + myVariable.ToString());
Response.End();
<snip>

ตัดสินจากสิ่งที่คุณโพสต์ในแง่ของการวิจัยฉันจะบอกว่ามันจะเป็นการออกแบบที่ไม่ดีถ้ามันต้องการการตอบสนองสุดท้าย


0

ใน asp แบบคลาสสิคฉันมี TTFB (Time To First Byte) ที่ 3 ถึง 10 วินาทีในการโทร ajax บางครั้งใหญ่กว่า TTFB ในหน้าปกติที่มีการเรียก SQL จำนวนมาก

Ajax ที่ส่งคืนคือส่วนของ HTML ที่จะแทรกเข้าไปในหน้าเว็บ

TTFB นั้นยาวกว่าเวลาเรนเดอร์หลายวินาที

ถ้าฉันเพิ่มการตอบกลับส่งหลังจากการแสดงผล TTFB ก็ลดลงอย่างมาก

ฉันสามารถใช้เอฟเฟกต์เดียวกันได้ด้วยการปล่อย "</body> </html>" แต่สิ่งนี้อาจไม่ทำงานเมื่อแสดงผล json หรือ xml; คำตอบที่นี่จำเป็นต้องใช้


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