성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
글쓰기
제목
이름
암호
전자우편
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'>C# - HttpClient.PostAsJsonAsync 호출 시 "Transfer-Encoding: chunked" 대신 "Content-Length" 헤더 처리</h1> <p> 테스트를 위해 ASP.NET Core Web API 프로젝트를 만든 후, <a target='tab' href='https://www.sysnet.pe.kr/2/0/13520'>HttpLoggingMiddleware를 활성화</a>시켜 둡니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using Microsoft.AspNetCore.HttpLogging; namespace WebApplication1; public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. <span style='color: blue; font-weight: bold'>builder.Services.AddHttpLogging(o => { o.LoggingFields = HttpLoggingFields.All & ~HttpLoggingFields.Duration; });</span> builder.Services.AddControllers(); var app = builder.Build(); <span style='color: blue; font-weight: bold'>app.UseHttpLogging();</span> // Configure the HTTP request pipeline. app.UseAuthorization(); app.MapControllers(); app.Run(); } } </pre> <br /> 이후, 다음과 같은 코드로 POST 요청을 보내면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > internal class Program { static void Main(string[] args) { HttpClient client = new HttpClient(); WeatherForecast item = new WeatherForecast { Date = new DateOnly(2021, 1, 1), TemperatureC = 0, Summary = "Freezing" }; client.BaseAddress = new Uri("http://localhost:5136/"); var response = <span style='color: blue; font-weight: bold'>client.PostAsJsonAsync</span>("/WeatherForecast", item).Result; Console.WriteLine(response.Content.ReadAsStringAsync().Result); } } </pre> <br /> 웹 서버 측의 화면에는 이런 로그가 남게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1] Request: Protocol: HTTP/1.1 Method: POST Scheme: http PathBase: Path: /WeatherForecast Host: localhost:5136 Content-Type: application/json; charset=utf-8 <span style='color: blue; font-weight: bold'>Transfer-Encoding: chunked</span> info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[3] RequestBody: {"date":"2021-01-01","temperatureC":0,"temperatureF":32,"summary":"Freezing"} info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2] Response: StatusCode: 200 </pre> <a name='post_async'></a> <br /> 동일한 요청을 PostAsync로 풀어보면 이렇게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var jsonContent = JsonContent.Create(item); var response = client.PostAsync("/WeatherForecast", jsonContent).Result; </pre> <br /> 위의 요청 역시 "Transfer-Encoding: chunked" 방식으로 처리됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그나저나, Transfer-Encoding이 아닌 Content-Length로 넘기는 방법은 없는 걸까요? 이에 대해 검색해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PostAsJsonAsync is always chunked ; <a target='tab' href='https://www.reddit.com/r/csharp/comments/v40z8s/postasjsonasync_is_always_chunked/'>https://www.reddit.com/r/csharp/comments/v40z8s/postasjsonasync_is_always_chunked/</a> </pre> <br /> LoadIntoBufferAsync 메서드를 호출하라고 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var jsonContent = JsonContent.Create(item); <span style='color: blue; font-weight: bold'>await jsonContent.LoadIntoBufferAsync();</span> var response = client.PostAsync("/WeatherForecast", jsonContent).Result; </pre> <br /> 실제로 이렇게 호출하면 다음과 같이 Content-Length로 바뀐 것을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1] Request: Protocol: HTTP/1.1 Method: POST Scheme: http PathBase: Path: /WeatherForecast Host: localhost:5136 Content-Type: application/json; charset=utf-8 <span style='color: blue; font-weight: bold'>Content-Length: 77</span> info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[3] RequestBody: {"date":"2021-01-01","temperatureC":0,"temperatureF":32,"summary":"Freezing"} info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2] Response: StatusCode: 200 </pre> <br /> 또 다른 방법은 JsonContent가 아닌 StringContent를 쓰는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var stringContent = new StringContent(JsonSerializer.Serialize(item), Encoding.UTF8, "application/json"); var response = client.PostAsync("/WeatherForecast", stringContent).Result; </pre> <br /> 이렇게 해도 Transfer-Encoding 대신 Content-Length로 처리한 요청이 날아갑니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 재미있는 점이 하나 있다면, PostAsJsonAsync 동작이 DelegatingHandler 내부의 코드에 따라 바뀔 수 있다는 점입니다. 가령, HTTP Client의 요청/응답을 로그로 남기기 위해 다음과 같은 식으로 LoggingHandler를 만들었다면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class LoggingHandler : DelegatingHandler { public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler) { } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Request 로깅 Console.WriteLine("Request:"); Console.WriteLine(request.ToString()); if (request.Content != null) { Console.WriteLine(<span style='color: blue; font-weight: bold'>request.Content.ReadAsStringAsync()</span>.Result); } Console.WriteLine(); // 호출 <span style='color: blue; font-weight: bold'>HttpResponseMessage response = await base.SendAsync(request, cancellationToken);</span> // Response 로깅 Console.WriteLine("Response:"); Console.WriteLine(response.ToString()); if (response.Content != null) { Console.WriteLine(await response.Content.ReadAsStringAsync()); } Console.WriteLine(); return response; } } </pre> <br /> PostAsJsonAsync으로 요청했는데도 불구하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HttpClientHandler sharedHandler = new HttpClientHandler(); HttpClient client = <span style='color: blue; font-weight: bold'>new HttpClient(new LoggingHandler(sharedHandler))</span>; WeatherForecast item = new WeatherForecast { Date = new DateOnly(2021, 1, 1), TemperatureC = 0, Summary = "Freezing" }; client.BaseAddress = new Uri("http://localhost:5136/"); var response = <span style='color: blue; font-weight: bold'>client.PostAsJsonAsync</span>("/WeatherForecast", item).Result; </pre> <br /> 서버 측에는 "Content-Length: 77" 헤더로 처리됩니다. 다시 말해, 부가적으로 설정한 DelegatingHandler가 의도치 않게 원래의 HTTP 통신에 간섭을 일으킨 것입니다. 원인은, SendAsync 호출 전 request.Content.ReadAsStringAsync() 호출을 했기 때문에 버퍼링 효과가 발생했던 것입니다.<br /> <br /> 그런 면에서, DelegatingHandler 제작자는 신중하게 코드를 작성할 필요가 있을 것 같습니다. 즉, 기왕이면 위와 같은 로그를 남기는 것도 순서를 다음과 같이 조정하는 것이 더 바람직한 코드가 되는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // 호출을 먼저 하고, HttpResponseMessage response = <span style='color: blue; font-weight: bold'>await base.SendAsync(request, cancellationToken);</span> // 이후 Request/Response 로깅 Console.WriteLine("Request:"); Console.WriteLine(request.ToString()); if (request.Content != null) { Console.WriteLine(request.Content.ReadAsStringAsync().Result); } Console.WriteLine(); Console.WriteLine("Response:"); Console.WriteLine(response.ToString()); if (response.Content != null) { Console.WriteLine(await response.Content.ReadAsStringAsync()); } Console.WriteLine(); return response; } </pre> </p><br /> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1658
(왼쪽의 숫자를 입력해야 합니다.)