Response.Redirect가 System.Threading.ThreadAbortException을 발생시키는 이유는 무엇입니까?


230

Response.Redirect (...)를 사용하여 양식을 새 페이지로 리디렉션하면 오류가 발생합니다.

mscorlib.dll에서 'System.Threading.ThreadAbortException'유형의 첫 번째 예외가 발생했습니다. mscorlib.dll에서
'System.Threading.ThreadAbortException'유형의 예외가 발생했지만 사용자 코드에서 처리되지 않았습니다.

이것에 대한 나의 이해는 웹 서버가 response.redirect가 호출 된 페이지의 나머지 부분을 중단하여 오류가 발생한다는 것입니다.

Response.RedirectendResponse라는 두 번째 매개 변수를 추가 할 수 있다는 것을 알고 있습니다 . endResponse를 True로 설정하면 여전히 오류가 발생하지만 False로 설정하면 그렇지 않습니다. 나는 그것이 웹 서버가 내가 리디렉션 한 나머지 페이지를 실행하고 있음을 의미한다고 확신합니다. 가장 적게 말하는 것은 비효율적 인 것 같습니다. 더 좋은 방법이 있습니까? 이외 Response.Redirect또는 난을받지 않습니다 경우 정지 하중 이전 페이지를 강제하는 방법은 무엇입니까 ThreadAbortException?

답변:


332

올바른 패턴은 endResponse = false를 사용하여 리디렉션 오버로드를 호출하고 컨트롤을 반환 한 후 IIS 파이프 라인이 EndRequest 단계로 직접 진행해야한다고 호출하는 것입니다.

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

Thomas Marquardt 의이 블로그 게시물 은 Application_Error 핸들러 내에서 리디렉션의 특수 사례를 처리하는 방법을 포함하여 추가 세부 사항을 제공합니다.


6
이후에 코드를 실행합니다 Context.ApplicationInstance.CompleteRequest();. 왜? 나는 할 것이다 return조건부로 이벤트 핸들러에서?
IsmailS

4
@Ismail : 이전 버전의 Redirect는 후속 코드 실행을 방지하기 위해 ThreadAbortException을 발생시킵니다. 더 선호되는 최신 버전은 발생하지 않지만 처리기에 추가 코드가 있으면 제어를 일찍 반환해야합니다.
Joel Fillmore

12
The old version of Redirect귀하의 의견에 사용하는 문구가 아니라 "두 번째 과부하"라고 말하는 것이 더 정확하다고 생각합니다 .MS가 구현을 변경 한 것이 아니라 다른 과부하입니다.
BornToCode

2
나는 이것이 이상적인 패턴이라고 생각하지 않습니다. 응답을 종료하지 말라고 페이지에 요청하고 실행을 계속 한 다음 프로그래밍 방식으로 요청을 완료합니다. 그러나 aspx 페이지 및 이벤트 핸들러 렌더링은 어떻습니까? 응답을 끝내지 않으면 "completeRequest ()"를 누르기 전에 aspx 페이지 렌더링이 완료됩니다. 이제 내 페이지에서 서버 측 속성을 사용하는 경우 유효한 로그인을 결정하기 위해 세션 변수를 말하면 만료되면 리디렉션하기 전에 null 예외가 발생합니다. 그리고 그것을 고치는 유일한 방법은 endResponse를 다시 true로 만드는 것입니다.
Abs

1
이 답변에 투표하려고했지만 해당 페이지 코드가 계속 실행되었습니다. 내 경우에는 이상적이지 않습니다. "ThreadAbortException"을 처리하거나 무시하는 것이 훨씬 더 깨끗합니다
DaniDev

159

ASP.Net WebForms 에는이 문제에 대한 간단하고 우아한 해결책 이 없습니다Redirect . Dirty 솔루션과 Tedious 솔루션 중에서 선택할 수 있습니다

Dirty : Response.Redirect(url)브라우저로 리디렉션을 보낸 다음 ThreadAbortedException현재 스레드를 종료 하기 위해 a 를 던집니다 . 따라서 Redirect () 호출 이후에는 코드가 실행되지 않습니다. 단점 : 나쁜 습관이며 이와 같은 스레드를 죽이는 성능에 영향을 미칩니다. 또한 ThreadAbortedExceptions예외 로깅에도 나타납니다.

Tedious : 권장되는 방법은 호출 Response.Redirect(url, false)한 다음 Context.ApplicationInstance.CompleteRequest()코드 실행이 계속되고 페이지 수명주기의 나머지 이벤트 핸들러가 계속 실행되는 것입니다. 예를 들어 Page_Load에서 리디렉션을 수행하는 경우 나머지 핸들러가 실행될뿐만 아니라 Page_PreRender 등도 계속 호출됩니다. 렌더링 된 페이지는 브라우저로 전송되지 않으므로 추가 처리를 피할 수 있습니다. 예를 들어, 페이지에서 플래그를 설정 한 후 처리하기 전에 후속 이벤트 핸들러가이 플래그를 확인하도록합니다.

"문서는 CompleteRequest" ASP.NET이 HTTP 파이프 라인 실행 체인에서 모든 이벤트와 필터링을 우회 하게한다 "고 설명합니다. 이것은 쉽게 오해 될 수 있습니다. 추가 HTTP 필터 및 모듈은 우회하지만 추가 이벤트는 우회하지 않습니다 현재 페이지 수명주기에서

더 깊은 문제는 WebForms에 추상화 수준이 없다는 것입니다. 이벤트 핸들러에있을 때 이미 출력 할 페이지를 작성하는 중입니다. 다른 페이지를 생성하기 위해 부분적으로 생성 된 페이지를 종료하기 때문에 이벤트 핸들러에서 경로 재 지정이 잘못됩니다. 제어 흐름이 렌더링 뷰와 분리되어 있기 때문에 MVC에는이 문제가 없으므로 뷰 RedirectAction를 생성하지 않고 컨트롤러에서 단순히 a 를 반환하여 깔끔한 리디렉션을 수행 할 수 있습니다.


7
내가 들었던 웹폼에 대한 최고의 설명은 "거짓 소스"라고 생각합니다.
mcfea

9
이 답변의 세부 사항을 좋아합니다. 허용 된 답변보다 낫습니다
Jess

dirty 옵션 을 사용하는 경우 Visual Studio에서 ThreadAbortException 중단을 해제 할 수 있습니다. DEBUG> 예외 ... . 확장 CLR>하여 System.Threading> 선택을 취소 System.Threading.ThreadAbortException을 .
Jess

정답이있는 사람이있어 주셔서 감사합니다.이 투표가 가장 높은 답변이어야합니다.
Abs

1
필자의 경우이 예외는 매번 발생하지 않으며 그 사이에 몇 번만 발생합니다. 의미 라이브 응용 프로그램의 동일한 버튼을 클릭하면 작동하지만 다른 컴퓨터에서 동일한 링크 및 동일한 버튼을 클릭하면 System.Threading.ThreadAbortException이 발생합니다. 왜 매번 일어나지 않는지 아십니까?
Sagar Shirke

33

늦어서 알고 있지만 나의 경우 나는 오직이 오류를 했어 Response.RedirectTry...Catch블록.

응답을 넣지 마십시오. Try ... Catch 블록으로 리디렉션하십시오. 나쁜 습관이야

편집하다

@Kiquenet의 의견에 따라 Response.Redirect를 Try ... Catch 블록에 넣는 대안으로 수행 할 작업이 있습니다.

방법 / 기능을 두 단계로 나눕니다.

Try ... Catch 블록 내의 1 단계는 요청 된 작업을 수행하고 "결과"값을 설정하여 작업의 성공 또는 실패를 나타냅니다.

Try ... Catch 블록 외부의 2 단계는 "결과"값에 따라 리디렉션을 수행하거나 수행하지 않습니다.

이 코드는 완벽하지는 않으며 테스트하지 않았으므로 복사해서는 안됩니다.

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.
    }
}

@ Kirkenet은 내가 할 일의 예를 위해 업데이트 된 답변을 참조하십시오. 최선의 방법은 말할 것도 없지만 가능한 대안이라고 생각합니다.
Ortund

내가 시도에 내 코드를 포장 할 때까지 문제가 없었어요, 캐치 ... 난 다른 코드 호출 .NET에서이 문제의 원인이 무엇인지 궁금
재미 - 이름 - 여기

8

Response.Redirect() 현재 요청을 중단하기 위해 예외를 발생시킵니다.

이 기술 자료 문서에서는 이 동작에 대해 설명합니다 ( Request.End()Server.Transfer()메소드 에도 해당 ).

들어 Response.Redirect()과부하가 존재한다 :

Response.Redirect(String url, bool endResponse)

endResponse = false 를 전달 하면 예외가 발생하지 않습니다 (그러나 런타임은 현재 요청을 계속 처리합니다).

경우 endResponse 해당 = (또는 다른 오버를 사용하는 경우)에서, 예외가 발생되어 현재의 요청은 즉시 종료된다.


7

여기 에 문제에 대한 공식적인 내용이 있습니다 (최신을 찾을 수는 없지만 이후 버전의 .net에서는 상황이 바뀌지 않았다고 생각합니다)


5
@svick 링크 썩음에 관계없이 링크 전용 답변은 실제로 큰 답변이 아닙니다. 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

이것은 Response.Redirect(url, true) 작동 방식입니다. ThreadAbortException스레드를 중단하기 위해 를 던집니다 . 그 예외를 무시하십시오. (나는 그것이 어디에서 보이는 전역 오류 처리기 / 로거라고 생각합니까?)

흥미로운 관련 토론 Response.End()해로운 것으로 간주됩니까? .


4
스레드 중단은 응답의 조기 종료를 처리 할 수있는 매우 어려운 방법 인 것 같습니다. 프레임 워크가 스레드를 대신하여 새 스레드를 회전시키는 대신 스레드를 재사용하는 것을 선호하지 않는 것이 이상합니다.
쓰셨

3

또한 다른 솔루션을 시도했지만 리디렉션 후 일부 코드가 실행되었습니다.

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

따라서 리디렉션 후 코드 실행을 방지 해야하는 경우

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

1
호르헤의 대답을 따르십시오. 이것은 스레드 중단 예외의 로깅을 제거합니다.
Maxim Lavrov

누군가 예외가 발생하는 이유를 물으면 try..catch로 게임을한다고 말하는 것은 답이 아닙니다. 허용 된 답변을 참조하십시오. "늦은 답변"을 검토하면서 답변에 댓글을 달았습니다
manuell

Response.Redirect의 두 번째 인수에 대해 false를 설정하는 것과 동일한 효과가 있지만 "false"는 ThreadAbortException을 캡처하는 것보다 더 나은 솔루션입니다. 이런 식으로해야 할 이유가 항상 없다고 생각합니다.
NickG

2

스레드에서 Abort를 수동으로 수행하는 경우를 대비하여이를 피하려고 시도했지만 "CompleteRequest"로 남겨두고 계속 진행합니다. 어쨌든 내 코드는 리디렉션 후 명령을 반환합니다. 이렇게 할 수 있습니다

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

내가하는 일은 다른 예외와 함께이 예외를 포착하는 것입니다. 이것이 누군가를 돕기를 바랍니다.

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

2
catch 보다 ThreadAbortException 예외를 피하는 것이 좋으며 아무것도하지 않습니까?
Kiquenet

-1

나도 그 문제가 있었다.

Server.Transfer대신에 사용해보십시오Response.Redirect

나를 위해 일했다.


2
Server.Transfer는 여전히 ThreadAbortException : support.microsoft.com/kb/312629를 발생 시켜야 하므로 권장되는 솔루션이 아닙니다.
Joel Beckham

9
Server.Transfer는 사용자에게 리디렉션을 보내지 않습니다. 그것은 완전히 다른 목적을 가지고 있습니다!
Marcel

1
Server.transfer와 responce.redirect는 다릅니다
mzonerz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.