Tại sao Phản hồi.Redirect gây ra System.Threading.ThreadAdortException?


230

Khi tôi sử dụng Feedback.Redirect (...) để chuyển hướng biểu mẫu của tôi sang một trang mới, tôi gặp lỗi:

Một ngoại lệ cơ hội đầu tiên của loại 'System.Threading.ThreadAdortException' đã xảy ra trong mscorlib.dll
Một ngoại lệ của loại 'System.Threading.ThreadAbortException' xảy ra trong mscorlib.dll nhưng không được xử lý trong mã người dùng

Sự hiểu biết của tôi về điều này là lỗi đang được gây ra bởi máy chủ web hủy bỏ phần còn lại của trang mà answer.redirect đã được gọi.

Tôi biết tôi có thể thêm một tham số thứ hai vào Response.Redirectđó được gọi là endResponse. Nếu tôi đặt endResponse thành True thì tôi vẫn gặp lỗi nhưng nếu tôi đặt thành Sai thì tôi không làm. Tôi khá chắc chắn mặc dù điều đó có nghĩa là máy chủ web đang chạy phần còn lại của trang mà tôi đã chuyển hướng đi. Mà dường như là không hiệu quả để nói rằng ít nhất. Có cách nào tốt hơn để làm điều này? Một cái gì đó khác hơn Response.Redirecthoặc có cách nào để buộc trang cũ ngừng tải mà tôi sẽ không nhận được ThreadAbortException?

Câu trả lời:


332

Mẫu chính xác là gọi quá tải Redirect bằng endResponse = false và thực hiện cuộc gọi để thông báo cho đường ống IIS rằng nó sẽ chuyển trực tiếp đến giai đoạn EndRequest sau khi bạn trả lại quyền kiểm soát:

Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();

Bài đăng trên blog này của Thomas Marquest cung cấp thêm chi tiết, bao gồm cách xử lý trường hợp đặc biệt chuyển hướng bên trong trình xử lý Application_Error.


6
Nó thực thi mã sau Context.ApplicationInstance.CompleteRequest();. Tại sao? Tôi sẽ phải returntừ xử lý sự kiện có điều kiện?
IsmailS

4
@Ismail: Phiên bản cũ của Redirect ném ThreadAdortException để ngăn thực thi bất kỳ mã tiếp theo nào. Phiên bản mới hơn, ưa thích không ném, nhưng bạn có trách nhiệm trả lại quyền kiểm soát sớm nếu bạn có thêm mã trong trình xử lý.
Joel Fillmore

12
Tôi nghĩ chính xác hơn khi nói "quá tải thứ hai" thay vì The old version of Redirectcụm từ bạn sử dụng trong nhận xét của mình, không giống như MS đã thay đổi cách thực hiện, đó chỉ là một tình trạng quá tải khác.
Sinh ra ToCode 14/07/2015

2
Tôi không nghĩ rằng đây là một mô hình lý tưởng. Bạn đang yêu cầu trang không kết thúc phản hồi và tiếp tục thực hiện và sau đó hoàn thành yêu cầu theo chương trình. Nhưng những gì về kết xuất của trang aspx và xử lý sự kiện? không kết thúc câu trả lời có nghĩa là nó sẽ kết thúc việc hiển thị trang aspx trước khi nhấn "CompleteRequest ()". Bây giờ nếu tôi đang sử dụng thuộc tính phía máy chủ trong trang của mình, hãy nói biến phiên để xác định đăng nhập hợp lệ, nếu hết hạn sẽ ném ngoại lệ null trước khi chuyển hướng. Và cách duy nhất để khắc phục điều đó là làm cho endResponse trở lại đúng.
Vắng

1
Sẽ bỏ phiếu cho câu trả lời này, nhưng mã trang đó vẫn tiếp tục được thực thi. Điều này không lý tưởng trong trường hợp của tôi. Sạch sẽ hơn nhiều để xử lý hoặc bỏ qua "
ThreadAdortException

159

Không giải pháp đơn giản và thanh lịch nào cho Redirectvấn đề trong ASP.Net WebForms. Bạn có thể chọn giữa giải pháp bẩn và giải pháp Tedious

Bẩn : Response.Redirect(url)gửi một chuyển hướng đến trình duyệt, và sau đó ném một ThreadAbortedExceptionđể chấm dứt chuỗi hiện tại. Vì vậy, không có mã nào được thực thi qua lệnh Redirect () -. Nhược điểm: Đó là thực tế xấu và có ý nghĩa hiệu suất để tiêu diệt các chủ đề như thế này. Ngoài ra, ThreadAbortedExceptionssẽ hiển thị trong đăng nhập ngoại lệ.

Tedious : Cách được đề xuất là gọi Response.Redirect(url, false)và sau đó Context.ApplicationInstance.CompleteRequest(), tuy nhiên, việc thực thi mã sẽ tiếp tục và phần còn lại của các trình xử lý sự kiện trong vòng đời của trang sẽ vẫn được thực thi. (Ví dụ: nếu bạn thực hiện chuyển hướng trong Page_Load, không chỉ phần còn lại của trình xử lý sẽ được thực thi, Page_PreRender, v.v. cũng sẽ vẫn được gọi - trang được hiển thị sẽ không được gửi đến trình duyệt. Bạn có thể tránh việc xử lý thêm bằng cách ví dụ: đặt cờ trên trang và sau đó để người xử lý sự kiện tiếp theo kiểm tra cờ này trước khi thực hiện bất kỳ xử lý nào.

(Tài liệu CompleteRequestnêu rõ rằng "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 ". Điều này có thể dễ bị hiểu lầm. Nó bỏ qua các bộ lọc và mô-đun HTTP hơn, nhưng nó không bỏ qua các sự kiện tiếp theo trong vòng đời của trang hiện tại .)

Vấn đề sâu xa hơn là WebForms thiếu một mức độ trừu tượng. Khi bạn đang ở trong một trình xử lý sự kiện, bạn đang trong quá trình xây dựng một trang để xuất. Chuyển hướng trong một trình xử lý sự kiện là xấu vì bạn đang chấm dứt một trang được tạo một phần để tạo một trang khác. MVC không gặp phải vấn đề này vì luồng điều khiển tách biệt với chế độ xem hiển thị, do đó bạn có thể thực hiện chuyển hướng sạch bằng cách chỉ cần trả lại một RedirectActiontrong bộ điều khiển mà không tạo ra chế độ xem.


7
Tôi tin rằng mô tả tốt nhất về các biểu mẫu web tôi từng nghe là "nước sốt nói dối".
mcfea

9
Tôi thích số lượng chi tiết trong câu trả lời này. Tốt hơn câu trả lời được chấp nhận
Jess

Nếu bạn sử dụng tùy chọn bẩn , Bạn có thể tắt ngắt trên ThreadAdortException trong Visual Studio. DEBUG> Exceptions ... . Mở rộng CLR> System.Threading> Bỏ chọn System.Threading.ThreadAdortException .
Jess

cảm ơn chúa, chúng tôi có một người có câu trả lời thích hợp, và đây sẽ là câu trả lời được bình chọn cao nhất.
Vắng

1
Trong trường hợp của tôi, ngoại lệ này không xảy ra cho mỗi lần, chỉ một vài lần ở giữa nó xảy ra. Có nghĩa là Nếu và nhấp vào cùng một nút của ứng dụng Live thì nó đang hoạt động nhưng khi cùng một liên kết và cùng một nút được nhấp từ máy khác, nó sẽ đưa ra System.Threading.ThreadAbortException. Bất cứ ý tưởng tại sao nó không xảy ra mọi lúc ??
Sagar Shirke

33

Tôi biết tôi đến trễ, nhưng tôi chỉ gặp lỗi này nếu tôi Response.Redirectở trong một Try...Catchkhối.

Không bao giờ đặt Phản hồi. Chuyển hướng vào khối Thử ... Bắt. Đó là thực hành xấu

Biên tập

Đáp lại bình luận của @ Kiquenet, đây là những gì tôi sẽ làm thay thế cho việc đưa Phản hồi. Chuyển hướng vào khối Thử ... Bắt.

Tôi chia phương thức / hàm thành hai bước.

Bước một bên trong khối Thử ... Bắt thực hiện các hành động được yêu cầu và đặt giá trị "kết quả" để biểu thị thành công hay thất bại của các hành động.

Bước hai bên ngoài khối Thử ... Bắt thực hiện chuyển hướng (hoặc không) tùy thuộc vào giá trị "kết quả" là gì.

Mã này không hoàn hảo và có lẽ không nên sao chép vì tôi chưa kiểm tra nó

public void btnLogin_Click(UserLoginViewModel model)
{
    bool ValidLogin = false; // this is our "result value"
    try
    {
        using (Context Db = new Context)
        {
            User User = new User();

            if (String.IsNullOrEmpty(model.EmailAddress))
                ValidLogin = false; // no email address was entered
            else
                User = Db.FirstOrDefault(x => x.EmailAddress == model.EmailAddress);

            if (User != null && User.PasswordHash == Hashing.CreateHash(model.Password))
                ValidLogin = true; // login succeeded
        }
    }
    catch (Exception ex)
    {
        throw ex; // something went wrong so throw an error
    }

    if (ValidLogin)
    {
        GenerateCookie(User);
        Response.Redirect("~/Members/Default.aspx");
    }
    else
    {
        // do something to indicate that the login failed.
    }
}

@Kiquenet vui lòng xem câu trả lời cập nhật của tôi để biết ví dụ về những gì tôi sẽ làm. Tôi không nói đó là khóa học tốt nhất, nhưng tôi nghĩ đó là một sự thay thế khả thi.
Ortund

Không có vấn đề gì cho đến khi tôi gói mã của mình trong một lần thử, bắt ... Tôi tự hỏi những gì các cuộc gọi mã khác gây ra hành vi này trong .NET
tên thú vị ở đây

8

Response.Redirect() ném một ngoại lệ để hủy bỏ yêu cầu hiện tại.

Bài viết KB này mô tả hành vi này (cũng cho các phương thức Request.End()Server.Transfer()).

Response.Redirect()có tồn tại quá tải:

Response.Redirect(String url, bool endResponse)

Nếu bạn vượt qua endResponse = false , thì ngoại lệ sẽ không bị ném (nhưng thời gian chạy sẽ tiếp tục xử lý yêu cầu hiện tại).

Nếu endResponse = true (hoặc nếu quá tải khác được sử dụng), ngoại lệ được đưa ra và yêu cầu hiện tại sẽ ngay lập tức bị chấm dứt.


7

Đây là dòng chính thức về vấn đề (Tôi không thể tìm thấy bản mới nhất, nhưng tôi không nghĩ tình hình đã thay đổi cho các phiên bản sau của .net)


5
@svick Bất kể liên kết thối, liên kết chỉ trả lời không phải là câu trả lời thực sự tuyệt vời. meta.stackexchange.com/q/8231 I think that links are fantastic, but they should never be the only piece of information in your answer.
Ryan Gates

7

Đây chỉ là cách làm Response.Redirect(url, true) việc. Nó ném ThreadAbortExceptionđể hủy bỏ các chủ đề. Chỉ cần bỏ qua ngoại lệ đó. (Tôi đoán đó là một trình xử lý lỗi / logger toàn cầu nơi bạn nhìn thấy nó?)

Một cuộc thảo luận liên quan thú vị được coi Response.End()có hại? .


4
Hủy bỏ một chủ đề có vẻ như một cách thực sự nặng tay để đối phó với kết thúc sớm của phản ứng. Tôi thấy lạ là khung không muốn sử dụng lại luồng thay vì quay lên một cái mới để thay thế nó.
tiêu

3

Ngoài ra tôi đã thử giải pháp khác, nhưng một số mã được thực thi sau khi chuyển hướng.

public static void ResponseRedirect(HttpResponse iResponse, string iUrl)
    {
        ResponseRedirect(iResponse, iUrl, HttpContext.Current);
    }

    public static void ResponseRedirect(HttpResponse iResponse, string iUrl, HttpContext iContext)
    {
        iResponse.Redirect(iUrl, false);

        iContext.ApplicationInstance.CompleteRequest();

        iResponse.BufferOutput = true;
        iResponse.Flush();
        iResponse.Close();
    }

Vì vậy, nếu cần ngăn chặn thực thi mã sau khi chuyển hướng

try
{
   //other code
   Response.Redirect("")
  // code not to be executed
}
catch(ThreadAbortException){}//do there id nothing here
catch(Exception ex)
{
  //Logging
}

1
Chỉ cần theo dõi bởi câu trả lời của Jorge. Điều này sẽ loại bỏ việc ghi nhật ký ngoại lệ Thread.
Maxim Lavrov

Khi ai đó hỏi tại sao anh ta bị Ngoại lệ, bảo anh ta chỉ chơi với thử..catch không phải là một câu trả lời. Xem câu trả lời được chấp nhận. Tôi đã nhận xét về câu trả lời của bạn trong khi xem xét "câu trả lời muộn"
manuell

Điều đó có tác dụng tương tự như đặt false cho đối số thứ 2 của Feedback.Redirect, nhưng "false" là một giải pháp tốt hơn so với việc nắm bắt ThreadAdortException. Tôi không thấy rằng có một lý do chính đáng để làm theo cách này.
NickG

2

tôi thậm chí đã cố gắng tránh điều này, chỉ trong trường hợp thực hiện Abort trên luồng theo cách thủ công, nhưng tôi muốn để nó với "CompleteRequest" và tiếp tục - mã của tôi vẫn trả về các lệnh sau khi chuyển hướng. Vì vậy, điều này có thể được thực hiện

public static void Redirect(string VPathRedirect, global::System.Web.UI.Page Sender)
{
    Sender.Response.Redirect(VPathRedirect, false);
    global::System.Web.UI.HttpContext.Current.ApplicationInstance.CompleteRequest();
}

1

Những gì tôi làm là bắt ngoại lệ này, cùng với một ngoại lệ khác có thể. Hy vọng điều này sẽ giúp ai đó.

 catch (ThreadAbortException ex1)
 {
    writeToLog(ex1.Message);
 }
 catch(Exception ex)
 {
     writeToLog(ex.Message);
 }

2
Tốt hơn nên tránh ngoại lệ ThreadAdortException hơn bắt và không làm gì ?
Kiquenet

-1

Tôi cũng gặp rắc rối.

Hãy thử sử dụng Server.Transferthay vìResponse.Redirect

Đã làm cho tôi.


2
Server.Transfer vẫn nên sử dụng ThreadAdortException: support.microsoft.com/kb/312629 , vì vậy đây không phải là giải pháp được đề xuất.
Joel Beckham

9
Server.Transfer sẽ không gửi chuyển hướng đến người dùng. Nó có một mục đích hoàn toàn khác!
Marcel

1
Server.transfer và responce.redirect khác nhau
mzonerz 17/8/2016
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.