Moet asp.net webapi 2 request en response body in een database loggen

Ik gebruik Microsoft Asp.net WebApi2 gehost op IIS. Ik wil heel eenvoudig de verzoektekst (XML of JSON) en de antwoordtekst voor elk bericht loggen.

Er is niets bijzonders aan dit project of de controller die de post verwerkt. Ik ben niet geïnteresseerd in het gebruik van logging-frameworks zoals nLog, elmah, log4net of de ingebouwde traceerfuncties van web-API, tenzij dit nodig is.

Ik wil gewoon weten waar ik mijn logcode moet plaatsen en hoe ik de daadwerkelijke JSON of XML kan krijgen van het inkomende en uitgaande verzoek en antwoord.

Mijn controller-postmethode:

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

Antwoord 1, autoriteit 100%

Ik zou aanraden om een ​​DelegatingHandlerte gebruiken. Dan hoef je je geen zorgen te maken over logcodes in je controllers.

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

Vervang gewoon Trace.WriteLinedoor uw logcode en registreer de handler in WebApiConfigals volgt:

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

Hier is de volledige Microsoft-documentatie voor Berichtafhandelaars.


Antwoord 2, autoriteit 8%

Er zijn meerdere benaderingen om de logboekregistratie van verzoeken/antwoorden voor elke WebAPI-methodeaanroep generiek af te handelen:

  1. ActionFilterAttribute:
    Men kan een aangepast ActionFilterAttributeschrijven en de controller/actiemethoden versieren om logging mogelijk te maken.

    Nadeel: je moet elke controller/methode versieren (je kunt het nog steeds op de basiscontroller doen, maar het lost nog steeds geen cross-cutting problemen op.

  2. Overschrijf BaseControlleren handel daar de logboekregistratie af.

    Con: we verwachten/dwingen de controllers om te erven van een aangepaste basiscontroller.

  3. DelegatingHandlergebruiken.

    Voordeel: we hebben het hier niet over controller/methode met deze benadering. Delegerende handler zit geïsoleerd en handelt op elegante wijze de aanvraag/antwoordregistratie af.

Raadpleeg voor een meer diepgaand artikel dit http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi.


Antwoord 3, autoriteit 7%

Een van de opties die je hebt, is het maken van een actiefilter en je WebApiController/ApiMethod ermee verfraaien.

Kenmerk filteren

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-controller

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

of

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

Hopelijk helpt dit.


Antwoord 4, autoriteit 2%

Het is eenvoudig om toegang te krijgen tot het verzoekbericht. Uw basisklasse, ApiControllerbevat .Requesteigenschap, die, zoals de naam al doet vermoeden, het verzoek in geparseerde vorm bevat. U onderzoekt het gewoon op wat u wilt loggen en geeft het door aan uw registratiefaciliteit, wat het ook is. Deze code kun je aan het begin van je actie plaatsen, als je het maar voor één of een handvol moet doen.

Als je het voor alle acties moet doen (alles betekent meer dan een handvol handvol), dan kun je de methode .ExecuteAsyncnegeren om elke actieoproep voor je controller vast te leggen.

p>

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

Antwoord 5

Dit lijkt een vrij oude thread te zijn, maar het is beter om een ​​andere oplossing te delen.

Je kunt deze methode toevoegen aan je global.asax-bestand dat elke keer wordt geactiveerd nadat het HTTP-verzoek is beëindigd.

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

Antwoord 6

Dit is een heel oud onderwerp, maar ik heb veel tijd (op internet gezocht) om deze dingen te doen, dus ik zal mijn oplossing hier posten.

Concept

  1. Overschrijf ExecuteAsync van de APIcontroller-methode voor het volgen van inkomende verzoeken, in mijn oplossing maak ik Base_ApiController als een ouder van de API-controllers van mijn project.
  2. Gebruik System.Web.Http.Filters.ActionFilterAttribute om de uitgaande reactie van de api-controller bij te houden
  3. ***(Aanvullend)***Gebruik System.Web.Http.Filters.ExceptionFilterAttribute om te loggen wanneer zich een uitzondering voordoet.

1. MijnController.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);
        }
    }
}

Other episodes