[c#] asp.net webapi 2 요청 및 응답 본문을 데이터베이스에 기록해야합니다.

IIS에서 호스팅되는 Microsoft Asp.net WebApi2를 사용하고 있습니다. 요청 본문 (XML 또는 JSON)과 각 게시물에 대한 응답 본문을 기록하고 싶습니다.

이 프로젝트 또는 게시물을 처리하는 컨트롤러에는 특별한 것이 없습니다. 필요한 경우가 아니면 nLog, elmah, log4net 또는 웹 API의 기본 제공 추적 기능과 같은 로깅 프레임 워크를 사용하는 데 관심이 없습니다.

로깅 코드를 어디에 넣을지, 수신 및 발신 요청 및 응답에서 실제 JSON 또는 XML을 얻는 방법을 알고 싶습니다.

내 컨트롤러 게시 방법 :

public HttpResponseMessage Post([FromBody])Employee employee)
{
   if (ModelState.IsValid)
   {
      // insert employee into to the database
   }

}



답변

나는 DelegatingHandler. 그러면 컨트롤러의 로깅 코드에 대해 걱정할 필요가 없습니다.

public class LogRequestAndResponseHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Content != null)
        {
            // log request body
            string requestBody = await request.Content.ReadAsStringAsync();
            Trace.WriteLine(requestBody);
        }
        // let other handlers process the request
        var result = await base.SendAsync(request, cancellationToken);

        if (result.Content != null)
        {
            // once response body is ready, log it
            var responseBody = await result.Content.ReadAsStringAsync();
            Trace.WriteLine(responseBody);
        }

        return result;
    }
}

Trace.WriteLine로깅 코드로 바꾸고 다음 과 WebApiConfig같이 핸들러를 등록하십시오 .

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

다음은 메시지 처리기에 대한 전체 Microsoft 설명서입니다 .


답변

모든 WebAPI 메서드 호출에 대한 요청 / 응답 로깅을 일반적으로 처리하는 방법에는 여러 가지가 있습니다.

  1. ActionFilterAttribute: 사용자 정의를 작성 ActionFilterAttribute하고 로깅을 활성화하기 위해 컨트롤러 / 액션 메서드를 장식 할 수 있습니다.

    단점 : 모든 컨트롤러 / 방법을 장식해야합니다 (여전히 기본 컨트롤러에서 할 수 있지만 교차 절단 문제는 해결되지 않습니다.

  2. BaseController거기에서 로깅을 무시 하고 처리합니다.

    단점 : 컨트롤러가 사용자 지정 기본 컨트롤러에서 상속 할 것으로 예상 / 강제합니다.

  3. 사용 DelegatingHandler.

    장점 : 여기서는이 접근 방식으로 컨트롤러 / 방법을 건드리지 않습니다. 위임 처리기는 격리되어 있으며 요청 / 응답 로깅을 정상적으로 처리합니다.

더 자세한 기사는이 http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi를 참조 하십시오 .


답변

옵션 중 하나는 액션 필터를 만들고 WebApiController / ApiMethod를 장식하는 것입니다.

필터 속성

public class MyFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.Request.Method == HttpMethod.Post)
            {
                var postData = actionContext.ActionArguments;
                //do logging here
            }
        }
    }

WebApi 컨트롤러

[MyFilterAttribute]
public class ValuesController : ApiController{..}

또는

[MyFilterAttribute]
public void Post([FromBody]string value){..}

도움이 되었기를 바랍니다.


답변

요청 메시지에 액세스하는 것은 쉽습니다. 귀하의 기본 클래스는ApiController 포함 .Request속성을 이름에서 알 수 있듯이, 구문 분석 된 형태로 요청을 포함. 로깅하려는 것이 무엇이든간에 검사하고 로깅 시설로 전달하기 만하면됩니다. 이 코드는 한 번 또는 몇 번만 수행해야하는 경우 작업 시작 부분에 넣을 수 있습니다.

모든 작업에 대해 수행해야하는 경우 (모두 관리 할 수있는 소수 이상의 의미), .ExecuteAsync컨트롤러에 대한 모든 작업 호출을 캡처하는 메서드를 재정의 하는 것입니다.

public override Task<HttpResponseMessage> ExecuteAsync(
    HttpControllerContext controllerContext,
    CancellationToken cancellationToken
)
{
    // Do logging here using controllerContext.Request
    return base.ExecuteAsync(controllerContext, cancellationToken);
}


답변

이것은 꽤 오래된 스레드처럼 보이지만 다른 솔루션을 공유하고 있습니다.

HTTP 요청이 종료 될 때마다 트리거되는 global.asax 파일에이 메서드를 추가 할 수 있습니다.

void Application_EndRequest(Object Sender, EventArgs e)
    {
        var request = (Sender as HttpApplication).Request;
        var response = (Sender as HttpApplication).Response;

        if (request.HttpMethod == "POST" || request.HttpMethod == "PUT")
        {


            byte[] bytes = request.BinaryRead(request.TotalBytes);
            string body = Encoding.UTF7.GetString(bytes);
            if (!String.IsNullOrEmpty(body))
            {


                // Do your logic here (Save in DB, Log in IIS etc.)
            }
        }
    }


답변

이것은 정말 오래된 주제이지만 이러한 작업을 수행하는 데 많은 시간 (인터넷 검색)을 보냈으므로 여기에 솔루션을 게시하겠습니다.

개념

  1. 인바운드 요청을 추적하기 위해 APicontroller 메서드의 ExecuteAsync를 재정의합니다. 솔루션에서 Base_ApiController를 프로젝트 API 컨트롤러의 부모로 만듭니다.
  2. System.Web.Http.Filters.ActionFilterAttribute를 사용하여 API 컨트롤러의 아웃 바운드 응답 추적
  3. *** (추가) *** System.Web.Http.Filters.ExceptionFilterAttribute를 사용하여 예외 발생시 기록합니다.

1. MyController.cs

    [APIExceptionFilter]  // use 3.
    [APIActionFilter]     // use 2.
    public class Base_APIController : ApiController
    {
        public   bool  IsLogInbound
        {
            get
            { return   ConfigurationManager.AppSettings["LogInboundRequest"] =="Y"? true:false ;     }
        }
        /// <summary>
        /// for logging exception
        /// </summary>
        /// <param name="controllerContext"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task<HttpResponseMessage> ExecuteAsync(
         HttpControllerContext controllerContext,
         CancellationToken cancellationToken
         )
        {
            // Do logging here using controllerContext.Request
            // I don't know why calling the code below make content not null Kanit P.
            var content = controllerContext.Request.Content.ReadAsStringAsync().Result.ToString(); // keep request json content
             // Do your own logging!
            if (IsLogInbound)
            {
                try
                {
                    ErrLog.Insert(ErrLog.type.InboundRequest, controllerContext.Request,
                         controllerContext.Request.RequestUri.AbsoluteUri
                         , content);
                }
                catch (Exception e) { }
            }

            // will not log err when go to wrong controller's action (error here but not go to APIExceptionFilter)
            var t = base.ExecuteAsync(controllerContext, cancellationToken);
            if (!t.Result.IsSuccessStatusCode)
            {
            }
            return t;

        }

2. APIActionFilter.cs

    public class APIActionFilter : System.Web.Http.Filters.ActionFilterAttribute
    {
        public bool LogOutboundRequest
        {
            get
            { return ConfigurationManager.AppSettings["LogInboundRequest"] == "Y" ? true : false; }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            try {

                var returndata = actionExecutedContext.Response.Content.ReadAsStringAsync().Result.ToString();
             //keep Json response content
             // Do your own logging!
                if (LogOutboundRequest)
                {
                    ErrLog.Insert(ErrLog.type.OutboundResponse, actionExecutedContext.Response.Headers,
                       actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName
                      + "/"
                      + actionExecutedContext.ActionContext.ActionDescriptor.ActionName
                      , returndata );
                }
            } catch (Exception e) {

            }


        }
    }
}

3. APIExceptionFilter.cs

    public class APIExceptionFilter : ExceptionFilterAttribute
    {
    public bool IsLogErr
    {
        get
        { return ConfigurationManager.AppSettings["LogExceptionRequest"] == "Y" ? true : false; }
    }


    public override void OnException(HttpActionExecutedContext context)
    {
        try
        {
            //Do your own logging!
            if (IsLogErr)
            {
                ErrLog.Insert(ErrLog.type.APIFilterException, context.Request,
                    context.ActionContext.ControllerContext.ControllerDescriptor.ControllerName
                    + "/"
                    + context.ActionContext.ActionDescriptor.ActionName
                    , context.Exception.ToString() + context.Exception.StackTrace);
            }
        }catch(Exception e){

        }

        if (context.Exception is NotImplementedException)
        {
            context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
        }
        else {
            context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError);

        }
    }
}


답변