2012-07-10 19 views
19

estoy tratando de crear un filtro para una [LoggedApiCall]Get() en una ApiController De acuerdo con esto: ASP.NET Web API ActionFilter exampleCómo agregar registro a MVC4 WebAPI

He creado una System.Web.HttpFilters.ActionFilterAttribute. La anulación permite OnActionExecuted(HttpActionExecutedContext actionExecutedContext)

Me parece que no puede encontrar una manera de obtener la IP de la persona que llama desde el HttpActionExecutedContext

Tal vez voy acerca del registro de cada llamada de API por el camino equivocado?

Respuesta

13

Utilizamos el siguiente filtro que agregamos a HttpConfiguration.Filters. Algunos código:

internal class LoggingFilter : IExceptionFilter, IActionFilter 
{ 
    private readonly ILog log; 

    public LoggingFilter(ILog log) 
    { 
     if (log == null) 
     { 
      throw new ArgumentNullException("log"); 
     } 

     this.log = log; 
    } 

    public bool AllowMultiple 
    { 
     get { return false; } 
    } 

    Task IExceptionFilter.ExecuteExceptionFilterAsync(HttpActionExecutedContext actionContext, CancellationToken cancellationToken) 
    { 
     if (actionContext == null) 
     { 
      throw new ArgumentNullException("actionContext"); 
     } 

     this.log.Error(string.Format("Unexpected error while executing {0}", this.BuildLogEntry(actionContext.ActionContext)), actionContext.Exception); 
     return TaskHelpers.Completed(); 
    } 

    Task<HttpResponseMessage> IActionFilter.ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) 
    { 
     if (actionContext == null) 
     { 
      throw new ArgumentNullException("actionContext"); 
     } 

     if (continuation == null) 
     { 
      throw new ArgumentNullException("continuation"); 
     } 

     if (!this.log.IsDebugEnabled) 
     { 
      // no point running at all if logging isn't currently enabled 
      return continuation(); 
     } 

     string logEntry = this.BuildLogEntry(actionContext); 
     IDisposable logContext = this.log.DebugTiming("Executing {0}", logEntry); 

     Task<string> requestContent; 
     if (actionContext.Request.Content != null) 
     { 
      requestContent = actionContext.Request.Content.ReadAsStringAsync().ContinueWith(requestResult => string.IsNullOrEmpty(requestResult.Result) ? "N/A" : requestResult.Result); 
     } 
     else 
     { 
      requestContent = TaskHelpers.FromResult("N/A"); 
     } 

     return requestContent.ContinueWith(
      requestResult => 
       { 
        this.log.DebugFormat("{0}, Request = {1}", logEntry, requestResult.Result); 

        return continuation() 
         .ContinueWith(t => 
          { 
           Task<string> responseContent; 
           if (t.IsCompleted && t.Result.Content != null) 
           { 
            responseContent = t.Result.Content.ReadAsStringAsync().ContinueWith(responseResult => string.IsNullOrEmpty(responseResult.Result) ? "N/A" : responseResult.Result); 
           } 
           else 
           { 
            responseContent = TaskHelpers.FromResult("N/A"); 
           } 

           return responseContent.ContinueWith(
            responseResult => 
             { 
              using (logContext) 
              { 
               this.log.DebugFormat("{0}, Status Code: {1}, Response = {2}", logEntry, t.Result.StatusCode, responseResult.Result); 
              } 

              return t.Result; 
             }); 
          }).Unwrap(); 
       }).Unwrap(); 
    } 

    /// <summary> 
    /// Builds log data about the request. 
    /// </summary> 
    /// <param name="actionContext">Data associated with the call</param> 
    private string BuildLogEntry(HttpActionContext actionContext) 
    { 
     string route = actionContext.Request.GetRouteData().Route.RouteTemplate; 
     string method = actionContext.Request.Method.Method; 
     string url = actionContext.Request.RequestUri.AbsoluteUri; 
     string controllerName = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName; 
     string actionName = actionContext.ActionDescriptor.ActionName; 

     return string.Format("{0} {1}, route: {2}, controller:{3}, action:{4}", method, url, route, controllerName, actionName); 
    } 
} 

Utilizamos log4net, que puede sustituir a la aplicación ILog con lo que mejor le parezca. ILog.DebugTiming es solo un método de extensión que usa un cronómetro para obtener el tiempo transcurrido para cada llamada.

Editar: En esta publicación Get the IP address of the remote host tiene detalles sobre cómo obtener la dirección IP de la persona que llama de forma remota.

Cheers, Dean

+0

El enlace que envió obras: GetClientIp cadena privada (HttpRequestMessage petición) \t \t { \t \t \t si (request.Properties.ContainsKey ("MS_HttpContext")) \t \t \t { \t \t \t \t return ((HttpContextWrapper) request.Properties ["MS_HttpContext"]). Request.UserHostAddress; \t \t \t} \t \t \t si (request.Properties.ContainsKey (RemoteEndpointMessageProperty.Name)) \t \t \t { \t \t \t \t RemoteEndpointMessageProperty prop; \t \t \t \t prop = (RemoteEndpointMessageProperty) request.Properties [RemoteEndpointMessageProperty.Name]; \t \t \t \t return prop.Address; \t \t \t} \t \t \t return null; \t } – maxfridbe

+0

Parece que la respuesta de Mike es la mejor opción: donde puede inyectar la implementación de registro real o incluso el marco de trabajo de API incorporado si así lo desea (y así evitar duplicar el esfuerzo). – Lex

+0

Investigamos los bits de 'ITraceWriter', pero parecía muy difícil correlacionar los tiempos de inicio y fin y parece haber una clara falta de implementaciones de fuerza de producción en la comunidad para guiar la escritura propia. Decidimos que el enfoque anterior era más simple. –

Cuestiones relacionadas