Phản hồi.End () được coi là có hại?


198

Điều KB này nói rằng ASP.NET Response.End()hủy bỏ một chủ đề.

Reflector cho thấy nó trông như thế này:

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();
        }
    }
}

Điều này có vẻ khá khắc nghiệt với tôi. Như bài viết KB nói, bất kỳ mã nào trong ứng dụng sau đây Response.End()sẽ không được thực thi và điều đó vi phạm nguyên tắc ít gây ngạc nhiên nhất. Nó gần giống như Application.Exit()trong một ứng dụng WinForms. Ngoại lệ hủy bỏ luồng gây ra bởi Response.End()không thể bắt được, vì vậy xung quanh mã trong try... finallysẽ không thỏa mãn.

Nó làm cho tôi tự hỏi nếu tôi luôn luôn nên tránh Response.End().

Bất cứ ai có thể đề nghị, khi nào tôi nên sử dụng Response.End(), khi nào Response.Close()và khi HttpContext.Current.ApplicationInstance.CompleteRequest()nào?

ref: mục blog của Rick Strahl .


Dựa trên đầu vào tôi nhận được, câu trả lời của tôi là Có, Response.Endcó hại , nhưng nó hữu ích trong một số trường hợp hạn chế.

  • sử dụng Response.End()như một cú ném không thể so sánh, để chấm dứt ngay lập tức HttpResponsetrong các điều kiện đặc biệt. Có thể hữu ích trong quá trình gỡ lỗi cũng. Tránh Response.End()để hoàn thành các phản ứng thường xuyên .
  • sử dụng Response.Close()để đóng ngay kết nối với máy khách. Per MSDN bài viết trên blog này , phương pháp này không dành cho xử lý yêu cầu HTTP bình thường. Rất khó có khả năng bạn có lý do chính đáng để gọi phương pháp này.
  • sử dụng CompleteRequest()để kết thúc một yêu cầu bình thường. CompleteRequestlàm cho đường ống dẫn ASP.NET nhảy tới EndRequestsự kiện, sau khi HttpApplicationsự kiện hiện tại hoàn thành. Vì vậy, nếu bạn gọi CompleteRequest, sau đó viết một cái gì đó nhiều hơn để trả lời, viết sẽ được gửi cho khách hàng.

Chỉnh sửa - ngày 13 tháng 4 năm 2011

Rõ ràng hơn có sẵn ở đây:
- Bài viết hữu ích trên Blog MSDN
- Phân tích hữu ích của Jon Reid


2
không biết điều gì đã thay đổi kể từ câu trả lời này, nhưng tôi đang nắm bắt Response.End ThreadAbortExceptiontốt.
Maslow

1
Hãy nhớ rằng Response.RedirectServer.Transfercả hai cuộc gọi Response.Endvà cũng nên tránh.
Owen Blacker

Câu trả lời:


66

Nếu bạn đã sử dụng một trình ghi nhật ký ngoại lệ trên ứng dụng của mình, nó sẽ được xử lý bằng ThreadAbortExceptioncác Response.End()cuộc gọi lành tính này . Tôi nghĩ rằng đây là cách nói của Microsoft "Gõ nó ra!".

Tôi sẽ chỉ sử dụng Response.End()nếu có một số điều kiện đặc biệt và không có hành động nào khác là có thể. Có lẽ sau đó, đăng nhập ngoại lệ này thực sự có thể chỉ ra một cảnh báo.


107

TL; DR

Ban đầu, tôi đã khuyên bạn chỉ nên thay thế tất cả các cuộc gọi của mình thành [Phản hồi.End] bằng các cuộc gọi [...] CompleteRequest (), nhưng nếu bạn muốn tránh xử lý trả sau và kết xuất html bạn sẽ cần thêm [.. .] ghi đè là tốt.

Jon Reid , "Phân tích cuối cùng"


Mỗi MSDN, Jon Reid và Alain Renon:

Hiệu suất ASP.NET - Quản lý ngoại lệ - Viết mã tránh ngoại lệ

Các phương thức Server.Transfer, Feedback.Redirect, Feedback.End đều đưa ra các ngoại lệ. Mỗi phương thức này đều gọi Phản hồi .End. Lần lượt, lệnh gọi tới Feedback.End sẽ gây ra ngoại lệ ThreadAdortException .

Giải pháp ThreadAdortException

HttpApplication.CompleteRequest () đặt một biến khiến cho luồng bỏ qua hầu hết các sự kiện trong đường ống sự kiện của httpApplication [-] không phải chuỗi sự kiện Trang mà là chuỗi sự kiện Ứng dụng.

...

tạo một biến cấp độ lớp gắn cờ nếu Trang nên chấm dứt và sau đó kiểm tra biến trước khi xử lý các sự kiện của bạn hoặc hiển thị trang của bạn. [...] Tôi khuyên bạn chỉ nên ghi đè các phương thức RaisePostBackEvent và Render

Phản hồi.End và Feedback. Đóng không được sử dụng trong xử lý yêu cầu thông thường khi hiệu suất là quan trọng. Phản hồi.End là một phương tiện thuận tiện, nặng tay để chấm dứt xử lý yêu cầu với hình phạt hiệu suất liên quan. Phản hồi. Đóng là để chấm dứt ngay lập tức phản hồi HTTP ở cấp IIS / socket và gây ra sự cố với những thứ như KeepAlive.

Phương thức được đề xuất để kết thúc một yêu cầu ASP.NET là HttpApplication.CompleteRequest. Hãy nhớ rằng kết xuất ASP.NET sẽ phải được bỏ qua một cách thủ công vì httpApplication.CompleteRequest bỏ qua phần còn lại của đường dẫn ứng dụng IIS / ASP.NET, không phải là đường dẫn Trang ASP.NET (là một giai đoạn trong đường dẫn ứng dụng).


Bản quyền © 2001-2007, C6 Software, Inc là tốt nhất tôi có thể nói.


Tài liệu tham khảo

HttpApplication.CompleteRequest

Làm cho ASP.NET bỏ qua tất cả các sự kiện và lọc trong chuỗi thực thi đường ống HTTP và trực tiếp thực hiện sự kiện EndRequest.

Phản hồi

Phương pháp này chỉ được cung cấp để tương thích với ASP Eclthat, để tương thích với công nghệ lập trình Web dựa trên COM có trước ASP.NET.preced ASP.NET. [Nhấn mạnh thêm]

Phản hồi. Đóng

Phương pháp này chấm dứt kết nối với máy khách một cách đột ngột và không dành cho xử lý yêu cầu HTTP thông thường . [Nhấn mạnh thêm]


4
> Hãy nhớ rằng kết xuất ASP.NET sẽ phải được bỏ qua một cách thủ công vì HttpApplication.CompleteRequest bỏ qua phần còn lại của đường dẫn ứng dụng IIS / ASP.NET, không phải là đường dẫn Trang ASP.NET (là một giai đoạn trong đường ống ứng dụng). Và làm thế nào để bạn thực hiện điều này?
PilotBob

1
Xem liên kết đến mã nơi Jon Reid trình bày cách đặt cờ và ghi đè các phương thức RaisePostBackEvent và Render của Trang để bỏ qua việc triển khai bình thường khi muốn. (Bạn có thể muốn làm điều này trong một lớp cơ sở tất cả các trang của ứng dụng của bạn nên được thừa hưởng từ.) Web.archive.org/web/20101224113858/http://www.c6software.com/...
user423430

4
Chỉ cần khôi phục lại: HttpApplication.CompleteRequest không chấm dứt phản hồi như Phản hồi. Tôi cũng vậy.
Glen Little

2
HttpApplication.CompleteRequest cũng không dừng dòng mã, vì vậy các dòng tiếp theo tiếp tục chạy. Điều đó có thể không ảnh hưởng đến những gì trình duyệt nhìn thấy, nhưng nếu những dòng đó thực hiện bất kỳ xử lý nào khác, nó có thể thực sự gây nhầm lẫn.
Joshua Frank

3
Tôi không thể nghĩ nhưng Web Forms bị phá vỡ bởi thiết kế. Điều gì làm giảm hiệu suất hơn, gọi Phản hồi.End () hoặc để trang tải mọi thứ và sau đó chặn phản hồi? Tôi không thể thấy Phản hồi ở đâu .End () "có hại" hơn ở đây. Hơn nữa, Microsoft coi `ThreadAdorticException 'là một sự kiện bình thường như hiển nhiên từ mã này: Referenceource.microsoft.com/#System.Web/UI/Page.cs,4875 Một điều chống lại Feedback.End () là nó có thể thất bại hủy bỏ phản hồi, đôi khi có thể dẫn đến phản hồi được hiển thị.
Ghasan

99

Câu hỏi này xuất hiện gần đầu tất cả các tìm kiếm của google về thông tin trên answer.end vì vậy đối với các tìm kiếm khác như tôi muốn đăng CSV / XML / PDF, v.v. để phản hồi cho một sự kiện mà không hiển thị toàn bộ trang ASPX, đây là cách tôi thực hiện . (ghi đè các phương thức kết xuất quá phức tạp đối với IMO tác vụ đơn giản như vậy)

// 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
Bạn không nên sử dụng trang APSX để làm điều này. Đó là rất nhiều nỗ lực lãng phí. Bạn phải sử dụng ASMX hoặc Dịch vụ web, bất cứ thứ gì ngoại trừ trang ASPX.
mattmanser

19
Đây dường như là câu trả lời với việc thực hiện dễ dàng nhất. Chìa khóa là Feedback.SuppressContent = True.
Chris Weber

3
@mattmanser - Không phải lúc nào cũng dễ dàng / tốt nhất / nên có một trang riêng cho các đại diện khác nhau của cùng một tài nguyên. Hãy suy nghĩ về REST, v.v ... Nếu khách hàng cho biết họ muốn csv, xml thông qua tiêu đề hoặc param, phương thức này chắc chắn sẽ là tốt nhất, trong khi vẫn cung cấp hỗ trợ html thông qua các phương tiện kết xuất thông thường của asp.net.
Chris Weber

1
Điều này đã không làm việc cho tôi. Tôi đã có một trang hoạt động với Feedback.End (), nhưng sử dụng tất cả các loại kết hợp của Feedback.Close (), Feedback.Flush (). HttpContext.Cản.ApplicationInstance.CompleteRequest () và nhiều thứ khác không hoạt động nếu tôi có bộ lọc GzipStream trên Phản hồi. Điều dường như đang xảy ra là trang vẫn đang được xuất cùng với tệp của tôi. Cuối cùng tôi đã vượt qua hàm Render () (để trống) và điều đó đã giải quyết nó cho tôi.
Aerik

1
CompleteRequest bỏ qua các phần của đường ống ứng dụng nhưng vẫn sẽ chạy qua phần còn lại của quá trình kết xuất trang, đây không phải là điểm dừng ngay lập tức như answer.end, duyên dáng hơn. Có nhiều giải thích sâu hơn về lý do tại sao trong các câu trả lời khác trên trang này.
Jay Zelos

11

Về câu hỏi "Tôi vẫn không biết sự khác biệt giữa Phản hồi. Đóng và CompleteRequest ()" tôi sẽ nói:

Không thích CompleteRequest (), không sử dụng Phản hồi. Đóng ().

Xem bài viết sau đây để biết tóm tắt về trường hợp này.

Xin lưu ý rằng ngay cả sau khi gọi CompleteRequest (), một số văn bản (ví dụ được sắp xếp lại từ mã ASPX) sẽ được thêm vào luồng đầu ra phản hồi. Bạn có thể ngăn chặn nó bằng cách ghi đè các phương thức Render và RaisePostBackEvent như được mô tả trong bài viết sau .

BTW: Tôi đồng ý với việc ngăn chặn sử dụng Feedback.End (), đặc biệt là khi ghi dữ liệu vào luồng http để mô phỏng tải xuống tệp. Trước đây, chúng tôi đã sử dụng Feedback.End () cho đến khi tệp nhật ký của chúng tôi chứa đầy ThreadAdortExceptions.


Tôi quan tâm đến việc ghi đè Kết xuất như bạn mô tả, nhưng liên kết đến "bài viết sau" đã chết. Có lẽ bạn có thể cập nhật mục nhập của bạn?
paqogomez

Xin lỗi vì trả lời muộn. Tôi không nhớ chính xác, những gì trong bài viết đó. Tuy nhiên, tôi đã tìm thấy nó trên webarchive.org: web.archive.org/web/20101224113858/http://www.c6software.com/...
Jan Šotola

9

Tôi không đồng ý với tuyên bố " Phản hồi . Và có hại ". Nó chắc chắn không có hại. Phản hồi. Hãy làm những gì nó nói; nó kết thúc thực hiện của trang. Sử dụng phản xạ để xem cách nó được thực hiện chỉ nên được xem như là hướng dẫn.


Khuyến nghị 2 tuổi của tôi
TRÁNH sử dụng Response.End()làm luồng điều khiển.
NÊN sử dụng Response.End()nếu bạn cần dừng thực thi yêu cầu và lưu ý rằng (thông thường) * không có mã nào sẽ thực thi qua điểm đó.


* Response.End()ThreadAdortException s.

Response.End() ném một ThreadAdortException như là một phần của việc triển khai hiện tại (như được ghi chú bởi OP).

ThreadAdortException là một ngoại lệ đặc biệt có thể bị bắt, nhưng nó sẽ tự động được nâng lên một lần nữa vào cuối khối bắt.

Để xem cách viết mã phải xử lý với ThreadAdortExceptions, hãy xem câu trả lời của @ Mehrdad cho SO Làm cách nào tôi có thể phát hiện một luồng xử lý luồng trong một khối cuối cùng nơi anh ấy tham chiếu Phương thức RuntimeHelpers.ExecuteCodeWithGuaranteedCleanupVùng thực thi bị ràng buộc


Bài báo Rick Strahl được đề cập là hướng dẫn, và đảm bảo đọc các bình luận là tốt. Lưu ý rằng vấn đề của Strahl là cụ thể. Anh ta muốn lấy dữ liệu cho khách hàng (một hình ảnh) và sau đó xử lý cập nhật cơ sở dữ liệu theo dõi lần truy cập mà không làm chậm việc phục vụ hình ảnh, điều này khiến anh ta gặp phải vấn đề làm gì đó sau khi Phản hồi.


Chúng tôi thấy điều này bài stackoverflow.com/questions/16731745/... gợi ý sử dụng Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () thay vì Response.End ()
jazzBox

3

Tôi chưa bao giờ cân nhắc sử dụng Feedback.End () để kiểm soát luồng chương trình.

Tuy nhiên, Feedback.End () có thể hữu ích, ví dụ như khi phân phát tệp cho người dùng.

Bạn đã viết tệp cho phản hồi và bạn không muốn bất cứ điều gì khác được thêm vào phản hồi vì nó có thể làm hỏng tệp của bạn.


1
Tôi hiểu sự cần thiết của một API để nói "phản hồi đã hoàn tất". Nhưng Phản hồi.End () cũng thực hiện hủy bỏ chuỗi. Đây là mấu chốt của câu hỏi. Khi nào thì nên kết hợp hai điều đó?
Cheeso

2

Tôi đã sử dụng Feedback.End () trong cả .NET và Classic ASP để kết thúc mọi thứ trước đó. Chẳng hạn, tôi sử dụng nó khi có số lượng đăng nhập cố định. Hoặc khi một trang bảo mật đang được truy cập từ thông tin đăng nhập không được xác thực (ví dụ sơ bộ):

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

Khi phân phát tệp cho người dùng Tôi sử dụng Flush, End có thể gây ra sự cố.


Hãy ghi nhớ, Flush () không phải là "đây là kết thúc". Nó chỉ là "tuôn ra mọi thứ cho đến nay." Lý do bạn có thể muốn "đây là kết thúc" là để cho khách hàng biết rằng nó có tất cả nội dung, trong khi máy chủ có thể đi và làm những việc khác - cập nhật tệp nhật ký, truy vấn bộ đếm cơ sở dữ liệu hoặc bất cứ điều gì. Nếu bạn gọi Feedback.Flush và sau đó thực hiện một trong những điều đó, khách hàng có thể tiếp tục chờ đợi thêm. Nếu bạn gọi Phản
hồi.End

Ngoài ra, bạn có thể sử dụng Phản hồi ghi đè.Redirect ("....", đúng) trong đó bool là 'endResponse: Cho biết liệu thực thi hiện tại của trang có nên chấm dứt hay không "
Robert Paulson

Tốt hơn hết là sử dụng khung Xác thực Mẫu để bảo vệ các trang được bảo mật bằng thông tin đăng nhập.
Robert Paulson

3
Trên thực tế, để tự sửa lỗi, tôi tin rằng mặc định của Feedback.Redirect và Server.Transfer là gọi Phản hồi. Trong nội bộ trừ khi bạn gọi ghi đè và chuyển 'false'. Cách mã của bạn được viết là Phản hồi. Không bao giờ được gọi ,
Robert Paulson

1
Feedback.end hoạt động khác rất nhiều trong .net so với trong ASP cổ điển. Trong .net, nó gây ra lỗi ngoại tuyến, có thể khá khó chịu.
Andy

2

Tôi chỉ sử dụng Feedback.End () làm cơ chế kiểm tra / gỡ lỗi

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

Đánh giá từ những gì bạn đã đăng về mặt nghiên cứu, tôi sẽ nói rằng nó sẽ là một thiết kế tồi nếu nó yêu cầu Phản hồi.


0

Trên asp cổ điển, tôi có TTFB (Time To First Byte) từ 3 đến 10 giây trên một số cuộc gọi ajax, lớn hơn nhiều so với TTFB trên các trang thông thường có nhiều lệnh gọi SQL hơn.

Các ajax được trả về là một đoạn HTML được đưa vào trang.

TTFB dài hơn vài giây so với thời gian kết xuất.

Nếu tôi thêm một phản hồi. Gửi sau khi kết xuất, TTFB đã giảm đáng kể.

Tôi có thể có được hiệu ứng tương tự bằng cách phát ra "</ body> </ html>", nhưng điều này có thể không hoạt động khi xuất ra json hoặc xml; ở đây đáp ứng.end là cần thiết.


Tôi biết đây là một câu trả lời cũ, nhưng một lần nữa, asp cổ điển đã cũ. Tôi thấy nó hữu ích ;-)
Leif Neland
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.