我正在使用IIS上托管的Microsoft Asp.net WebApi2。我非常希望记录请求体(xml或json)以及每个帖子的响应体。
此项目或处理帖子的控制器没有什么特别之处。我对使用nLog,elmah,log4net等日志框架或webapi的内置跟踪功能不感兴趣,除非有必要这样做。
我只是想知道在哪里放置我的日志代码以及如何从传入和传出的请求和响应中获取实际的json或xml。
我的控制器发布方法:
public HttpResponseMessage Post([FromBody])Employee employee)
{
if (ModelState.IsValid)
{
// insert employee into to database
}
}
答案 0 :(得分:169)
我建议使用DelegatingHandler
。然后,您无需担心控制器中的任何日志记录代码。
public class LogRequestAndResponseHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// 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());
答案 1 :(得分:13)
有多种方法可以为每个WebAPI方法调用一般性地处理请求/响应日志记录:
ActionFilterAttribute
:
可以编写自定义ActionFilterAttribute
并修饰控制器/操作方法以启用日志记录。
Con:你需要装饰每个控制器/方法(你仍然可以在基础控制器上完成它,但它仍然没有解决交叉问题。
覆盖BaseController
并处理日志记录。
Con:我们期待/强制控制器继承自定义基本控制器。
使用DelegatingHandler
。
优点:我们没有采用这种方法来触及控制器/方法。委托处理程序处于隔离状态,可以正常处理请求/响应日志记录。
有关更深入的文章,请参阅此http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi。
答案 2 :(得分:7)
您拥有的一个选项是使用创建动作过滤器并使用它来装饰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控制器
[MyFilter]
public class ValuesController : ApiController{..}
或
[MyFilter]
public void Post([FromBody]string value){..}
希望这有帮助。
答案 3 :(得分:3)
轻松访问请求消息。您的base class, ApiController
包含.Request
property,顾名思义,它包含已解析格式的请求。您只需检查它以查找记录并将其传递到您的记录设施的任何内容,无论它们是什么。如果你需要只为一个或少数人做这个代码,你可以把这个代码放在行动的开头。
如果您需要在所有操作上执行此操作(所有操作都超过可管理的操作),那么您可以执行的操作是覆盖.ExecuteAsync
方法以捕获控制器的每个操作调用。
public override Task<HttpResponseMessage> ExecuteAsync(
HttpControllerContext controllerContext,
CancellationToken cancellationToken
)
{
// Do logging here using controllerContext.Request
return base.ExecuteAsync(controllerContext, cancellationToken);
}
答案 4 :(得分:0)
这似乎是一个很老的话题,但愿意分享另一个解决方案。
您可以将此方法添加到您的global.asax文件中,该方法将在HTTP请求结束后每次触发。
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.)
}
}
}
答案 5 :(得分:0)
这确实是一个老话题,但是我花了很多时间(搜索互联网)来做这些事情,所以我将解决方案发布在这里。
概念
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);
}
}
}