2013-03-19 5 views
9

웹 API 요청 콘텐츠 (예 : json 문자열)를 로그 아웃하려고합니다. ITraceWriter 클래스 (example)를 구현하고이를 웹 API가 파이프 라인에서 호출하도록 구성했습니다. 그러나 요청을 읽으면 내용을 읽거나 스트림으로 복사하여 null 모델이되는 메서드에 사용할 수 없습니다. This post 그 문제에 대해 조금 이야기합니다. 누구나 인바운드 웹 API 요청 콘텐츠를 로깅 한 경험이 있으며 최상의 접근 방식이 무엇인지 알고 있습니까?ASP.NET 웹 API 인바운드 요청 내용 로깅

감사

업데이트는

나는 내 프로젝트에서 어떤 것도 배제하는 간단한 샘플 웹 API 프로젝트를 생성하고 난 여전히 모델 때문에 로깅 null이 될 것입니다 것을 알 수있다. 나는 Fidder를 통해 게시하여 몇 번 연속 테스트하고 내 모델이 null로 표시되는지 확인합니다. 중단 점이 제자리에 있으면 작동 할 수도 있기 때문에 동기/타이밍 문제가 있다고 생각합니다. 이 방법을 사용하는 방법에 대한 의견이 있으십니까?

헤더 :

User-Agent: Fiddler 
Host: localhost:56824 
Content-Type: application/json 
Content-Length: 22 

바디 :

컨트롤러 :

public class ValuesController : ApiController 
{ 
    [HttpPost] 
    public void Post(ValuesModel model) 
    { 
     if (model == null) 
     { 
      Debug.WriteLine("model was null!"); 
     } 
     else 
     { 
      Debug.WriteLine("model was NOT null!"); 
     } 
    } 
} 

모델 :

0 여기
{ 
"A":1,"B":"test" 
} 

코드입니다
public class ValuesModel 
{ 
    public int A { get; set; } 
    public string B { get; set; } 
} 

로거 :

public class APITraceLogger : DelegatingHandler 
    { 
     protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) 
     { 
      if (request.Content != null) 
      { 
       // This can cause model to be null 
       request.Content.ReadAsStringAsync().ContinueWith(s => 
       { 
        string requestText = s.Result; 
        Debug.WriteLine(requestText); 
       }); 

       // and so can this 
       //request.Content.ReadAsByteArrayAsync() 
       // .ContinueWith((task) => 
       // { 
       //  string requestText = System.Text.UTF8Encoding.UTF8.GetString(task.Result); 
       //  Debug.WriteLine(requestText); 
       // }); 
      } 
      // Execute the request, this does not block 
      var response = base.SendAsync(request, cancellationToken); 

      // TODO: 
      // Once the response is processed asynchronously, log the response data 
      // to the database 


      return response; 
     } 


    } 

WebApiConfig 클래스의 배선까지 로거 :

config.MessageHandlers.Add(new APITraceLogger()); 

업데이트 B 난에 로거를 변경하는 경우 지금 노력하고 있습니다 것 같다

다음 코드는 await, async를 추가하고 결과를 반환합니다. 비동기 코드 나 진정한 타이밍 문제 등을 이해하지 못하는 것처럼 보입니다.

public class APITraceLogger : DelegatingHandler 
{ 
    protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) 
    { 
     if (request.Content != null) 
     { 

      // This does seem to work - is it because it is synchronous? Is this a potential problem? 
      var requestText = await request.Content.ReadAsStringAsync(); 
      Debug.WriteLine(requestText); 
     } 
     // Execute the request, this does not block 
     var response = base.SendAsync(request, cancellationToken); 

     // TODO: 
     // Once the response is processed asynchronously, log the response data 
     // to the database 


     return response.Result; 
    } 


} 

답변

5

Filips는 ReadAsiasAsync 또는 ReadAsByteArrayAsync 메서드에서 Filip이 요청 내용을 내부적으로 버퍼링합니다. 즉, 들어오는 요청의 스트림 유형이 버퍼되지 않은 스트림 인 경우에도 메시지 처리기에서 ReadAsStringAsync/ReadAsByteArrayAsync를 안전하게 수행하고 모델 바인딩이 정상적으로 작동 할 것으로 기대할 수 있습니다.

기본적으로 요청의 스트림은 웹 호스트와 자체 호스트 모두에서 버퍼링됩니다. 위 ... 그냥 참고로

public class CustomBufferPolicySelector : WebHostBufferPolicySelector 
{ 
    public override bool UseBufferedInputStream(object hostContext) 
    { 
     //NOTE: by default, the request stream is always in buffered mode. 
     //return base.UseBufferedInputStream(hostContext); 

     return false; 
    } 
} 

config.Services.Replace(typeof(IHostBufferPolicySelector), new CustomBufferPolicySelector()); 

:하지만 당신은 ReadAsStringAsync/ReadAsByteArrayAsync 및 모델의 명령을 행한다도 비 버퍼 모드에서 잘 작동 사용 여부를 확인하려는 경우, 당신은 비 버퍼 모드를 강제로 다음과 같은 작업을 수행 할 수 정책 선택자는 현재 웹 호스트에서만 작동합니다.당신이 SelfHost에서 비슷한 테스트를하려는 경우, 다음을 수행하십시오

//NOTE: by default, the transfer mode is TransferMode.Buffered 
config.TransferMode = System.ServiceModel.TransferMode.StreamedRequest; 

위 게시물의 업데이트 B 후 :

public class LoggingHandler : DelegatingHandler 
{ 
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     if (request.Content != null) 
     { 
      string requestContent = await request.Content.ReadAsStringAsync(); 
     } 

     HttpResponseMessage response = await base.SendAsync(request, cancellationToken); 

     if (response.Content != null) 
     { 
      string responseContent = await response.Content.ReadAsStringAsync(); 
     } 

     return response; 
    } 
} 
: 아래와 같은

당신은 당신의 핸들러를 수정할 수 있습니다

+0

Filip에 의해 그 주석에 의해 던졌습니다. 내가 ReadAsStringAsync를 사용하고 내 모델이 null이 될 것입니다. ITraceWriter 구현에서 사용한 기본 코드는 다음과 같습니다. request.Content.ReadAsStringAsync(). ContinueWith (s => { string requestText = s.Result; Logger.Log (requestText); }}); – Bryan

+0

당신이 언급 한 문제를 재현 할 수 없습니다. 예를 들어 (최선의 방법은 아닙니다), Mike Wasson의 샘플에서 SimpleTracer의 WriteTrace 메서드에 다음 코드가 있습니다. if (rec.Request! = null) {Console.WriteLine (rec.Category + ","+ rec.Request.Content.ReadAsStringAsync(). 결과); } –

+0

다시 해봐 줘서 고마워. 무엇이 다른지 알아 내려고하는 중입니다. 내 로컬 dev에 대한 MVC 4 및 IIS Express에서 실행중인 사용하고 있습니다. 아마 IIS Express가 그 차이 일 것입니다. 나는 다른 것을 시도하고 다시 게시 할 것입니다. – Bryan