성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>ASP.NET - Server cannot append header after HTTP headers have been sent. (HTTP 헤더를 보낸 후에는 서버에서 헤더를 추가할 수 없습니다.)</h1> <p> ASP.NET 응용 프로그램 개발 중 다음과 같은 예외를 만날 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HTTP 헤더를 보낸 후에는 서버에서 헤더를 추가할 수 없습니다. [HttpException (0x80004005): Server cannot append header after HTTP headers have been sent.] System.Web.HttpResponse.AppendHeader(String name, String value) +9691456 System.Web.HttpResponse.AddHeader(String name, String value) +13 ...[생략]... System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +141 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69 </pre> <br /> 원인은 간단합니다. HTTP 프로토콜의 특성 상, HTTP Header 또는 Body까지 이미 모두 내려보냈는데 뒤늦게 HTTP 헤더를 추가하는 등의 작업을 하면 예외가 발생하는 것입니다.<br /> <br /> 간단하게 예를 들어볼까요? ^^<br /> <br /> 오류 상황을 재현하기 위해, Default.aspx.cs에 다음과 같은 코드를 추가해 줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; namespace WebApplication1 { public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>this.Response.Flush();</span> } } } </pre> <br /> Response.Flush 메서드는 현재까지의 응답 Stream에 담긴 모든 내용을 클라이언트로 전송하는 역할을 합니다. 당연히 이 메서드로 인해 HTTP Header 영역이 내려가게 됩니다. 정말 그런지 ^^ 확인해 볼까요? 이를 위해 다음과 같이 약간의 코드를 더 추가한 후,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; namespace WebApplication1 { public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { this.Response.Flush(); <span style='color: blue; font-weight: bold'>Thread.Sleep(1000 * 15); // 앞 단의 Stream이 출력된 후 HTTP 응답이 완전히 완료되기까지 15초 동안 대기</span> } } } </pre> <br /> 웹 브라우저의 "개발자 도구"와 함께 방문해 "Network" 영역을 보면, Flush 코드가 수행된 시점에 HTTP Header 내용이 보이면서 (Pending) 상태로 15초 동안 머무르는 것을 볼 수 있습니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='add_header_1.png' src='/SysWebRes/bbs/add_header_1.png' /><br /> <br /> 이제 Global.asax 파일에 EndRequest 메서드를 생성하고 다음과 같이 Header를 추가해 봅니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Web; namespace WebApplication1 { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { } protected void Application_EndRequest(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>HttpContext.Current.Response.AddHeader("test", "value");</span> } } } </pre> <br /> 당연히, 이후부터는 default.aspx를 방문할 때마다 "HttpException (0x80004005): Server cannot append header after HTTP headers have been sent" 예외가 발생합니다.<br /> <br /> 따라서, Response.AddHeader(또는, AppendHeader/ClearHeaders)와 같은 메서드를 공통 라이브러리 같은 곳에서 호출하려면 반드시 try/catch를 해주는 것이 좋습니다.<br /> <br /> <hr style='width: 50%' /> <br /> 그런데, try/catch만 해주는 것은 왠지 아쉽습니다. 애초부터 header를 쓸 수 없는 상황임을 알 수 있다면 좋을 텐데요. 이를 위해 HeadersWritten 속성이 .NET Framework 4.5.2부터 제공됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HttpResponse.HeadersWritten Property ; <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/system.web.httpresponse.headerswritten'>https://docs.microsoft.com/en-us/dotnet/api/system.web.httpresponse.headerswritten</a> </pre> <br /> 이를 이용하면 AddHeader 메서드를 좀 더 매끄럽게 호출할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > protected void Application_EndRequest(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>if (HttpContext.Current.Response.HeadersWritten == false)</span> { HttpContext.Current.Response.AddHeader("test", "value"); } } </pre> <br /> 4.5.2 미만의 버전에서는 HeadersWritten 속성이 "internal"로 되어 있기 때문에 접근할 수 없습니다. 대신, 어차피 공용 속성으로 변경되었으니 향후 호환성을 걱정하지 않아도 되기 때문에 .NET Reflection을 이용해 사용하는 것도 방법일 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Reflection; using System.Web; namespace WebApplication1 { public class Global : System.Web.HttpApplication { static PropertyInfo _piHeadersWritten = null; protected void Application_Start(object sender, EventArgs e) { if (_piHeadersWritten == null) { _piHeadersWritten = typeof(HttpResponse).GetProperty("HeadersWritten", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); } } protected void Application_EndRequest(object sender, EventArgs e) { bool headersWritten = GetHeadersWritten(); if (headersWritten == false) { HttpContext.Current.Response.AddHeader("test", "value"); } } bool GetHeadersWritten() { if (_piHeadersWritten == null) { return false; } return (bool)_piHeadersWritten.GetValue(HttpContext.Current.Response); } } } </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1119&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1196
(왼쪽의 숫자를 입력해야 합니다.)