ASP .NET Web API: Trace Listeners: HTTP/1.1 500 Internal Server Error: What's wrong?
ASP .NET Web API: Trace Listeners: HTTP/1.1 500 Internal Server Error: What's wrong?
我使用 .NET 已经有一段时间了,但这是我的第一个 Web API 2 项目。这是一场艰苦的斗争,所以请对我宽容点。关于 Web API 的一些事情可能是外面的文章假设已经知道的、显而易见的或显而易见的,但实际上直到您尝试发现,例如,“{id}”参数为 "magic",应尽可能使用。
考虑到这一点,为了帮助进一步排除我们的应用程序故障,我尝试使用 Trace Listeners 和 NLog 启用日志记录,并使用几篇优秀文章作为指南,包括以下两篇:
我正在尝试使用 Trace Listeners 和 NLog 为 运行 获取 ASP .NET Web API 服务,以帮助我们在应用程序中记录性能关键点并为其添加时间戳很慢。
但是,当我从 Fiddler 测试我的控制器时,出现如下异常:
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 04 Aug 2017 18:45:11 GMT
Content-Length: 3617
{"Message":"An error has occurred.",
"ExceptionMessage":"Value cannot be null.\r\nParameter name: traceWriter","ExceptionType":"System.ArgumentNullException","StackTrace":" at System.Web.Http.Tracing.ITraceWriterExtensions.Trace(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String messageFormat, Object[] messageArguments)\r\n at PURL_WebMarketing_API.Controllers.VisitorFormController.<GetFormInfo>d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---" }
我正在使用 Visual Studio 2015、.NET Framework 4.5 和用于 NLog、ASP .NET Web API 和 ASP 的最新 NuGet 包。 NET Web API 跟踪。
现在是代码...
来自 App_Start/WebApiConfig.cs:
// Enable tracing
config.EnableSystemDiagnosticsTracing();
// Replace the default trace writer with my own custom handler
config.Services.Replace(typeof(System.Web.Http.Tracing.ITraceWriter), new Utilities.TraceHandler());
在VisitorFormController.cs中:
public class VisitorFormController : ApiController
{
private IDataAccessRepository _repository;
private readonly ITraceWriter _tracer;
public VisitorFormController(IDataAccessRepository repository): base()
{
_repository = repository;
if (Request != null)
{
_tracer = Request.GetConfiguration().Services.GetTraceWriter();
}
}
[HttpGet]
[Route("api/VisitorForm/{code}/{pageType}")]
[EnableCors(origins: "*", headers: "*", methods: "options,get")]
public async Task<IHttpActionResult> GetFormInfo(string code, string pageType)
{
DateTime startTime = DateTime.Now;
if (string.IsNullOrEmpty(code))
return BadRequest("Invitation code is required.");
if (!IsValidPageType(pageType))
return BadRequest("Valid page types may be 'post', 'info', 'No Code' or blank.");
if (Request != null)
{
string startLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardStartEventNames.EVENT_TYPE_VISITOR_FORM_REQ_START);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, startLogMessage);
}
// *** Work happens here
RecipientModel visitorFormInformation = _repository.GetVisitorFormInformation(code, pageType);
// ***
if (visitorFormInformation == null)
return NotFound();
if (Request != null)
{
string endLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardEndEventNames.EVENT_TYPE_VISITOR_FORM_REQ_END, startTime);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, endLogMessage);
}
try
{
DateTime startLogActivityTime = DateTime.Now;
if (Request != null)
{
string startLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardStartEventNames.EVENT_TYPE_UPDATE_VISIT_LOG_FORM_VIEWED_START);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, startLogMessage);
}
// Log visitor activity, asynchronously
await _repository.LogActivity(visitorFormInformation.RecipientId, visitorFormInformation.IsCompleted);
if (Request != null)
{
string endLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardEndEventNames.EVENT_TYPE_UPDATE_VISIT_LOG_FORM_VIEWED_END, startLogActivityTime);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, endLogMessage);
}
}
catch
{
return BadRequest("Recipient ID#" + visitorFormInformation.RecipientId.ToString() + " not found.");
}
return Ok(visitorFormInformation);
}
要查看我的自定义跟踪处理程序,请参阅上面发布的 Filip Wojcieszyn 的博客文章。我的版本几乎相同,因此为了简洁起见,我省略了它。
如果我在使用跟踪侦听器方面完全按照书上的规定进行操作,那么它会不会是我传递的内容?在当前状态下,只要禁用跟踪侦听器,该服务就可以工作。
在有人问之前,我已经在各种浏览器中试过了。但我在机智的帮助下,将不胜感激任何有用的评论和建议。谢谢!
问题是 HttpContext 在控制器的构造函数中还不可用,这就是 Request
属性 为 null 的原因,这导致 _tracer
为 null,这会导致对 _tracer
的任何函数调用抛出 ArgumentNullException
.
我建议您阅读更多有关 WebApi 管道的内容,例如 here。它告诉您控制器是如何构建的,以及当请求入站时在什么时候发生了什么。
简而言之:解决方案是不将 _tracer
存储在控制器级别,而是存储在正在执行的操作中,并在每次操作调用时获取 'new' 跟踪器。
public class VisitorFormController : ApiController
{
private IDataAccessRepository _repository;
public VisitorFormController(IDataAccessRepository repository): base()
{
_repository = repository;
}
[HttpGet]
[Route("api/VisitorForm/{code}/{pageType}")]
[EnableCors(origins: "*", headers: "*", methods: "options,get")]
public async Task<IHttpActionResult> GetFormInfo(string code, string pageType)
{
var tracer = Request.GetConfiguration().Services.GetTraceWriter();
// Use tracer from here on...
}
}
我使用 .NET 已经有一段时间了,但这是我的第一个 Web API 2 项目。这是一场艰苦的斗争,所以请对我宽容点。关于 Web API 的一些事情可能是外面的文章假设已经知道的、显而易见的或显而易见的,但实际上直到您尝试发现,例如,“{id}”参数为 "magic",应尽可能使用。
考虑到这一点,为了帮助进一步排除我们的应用程序故障,我尝试使用 Trace Listeners 和 NLog 启用日志记录,并使用几篇优秀文章作为指南,包括以下两篇:
我正在尝试使用 Trace Listeners 和 NLog 为 运行 获取 ASP .NET Web API 服务,以帮助我们在应用程序中记录性能关键点并为其添加时间戳很慢。
但是,当我从 Fiddler 测试我的控制器时,出现如下异常:
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 04 Aug 2017 18:45:11 GMT
Content-Length: 3617
{"Message":"An error has occurred.",
"ExceptionMessage":"Value cannot be null.\r\nParameter name: traceWriter","ExceptionType":"System.ArgumentNullException","StackTrace":" at System.Web.Http.Tracing.ITraceWriterExtensions.Trace(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String messageFormat, Object[] messageArguments)\r\n at PURL_WebMarketing_API.Controllers.VisitorFormController.<GetFormInfo>d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---" }
我正在使用 Visual Studio 2015、.NET Framework 4.5 和用于 NLog、ASP .NET Web API 和 ASP 的最新 NuGet 包。 NET Web API 跟踪。
现在是代码...
来自 App_Start/WebApiConfig.cs:
// Enable tracing
config.EnableSystemDiagnosticsTracing();
// Replace the default trace writer with my own custom handler
config.Services.Replace(typeof(System.Web.Http.Tracing.ITraceWriter), new Utilities.TraceHandler());
在VisitorFormController.cs中:
public class VisitorFormController : ApiController
{
private IDataAccessRepository _repository;
private readonly ITraceWriter _tracer;
public VisitorFormController(IDataAccessRepository repository): base()
{
_repository = repository;
if (Request != null)
{
_tracer = Request.GetConfiguration().Services.GetTraceWriter();
}
}
[HttpGet]
[Route("api/VisitorForm/{code}/{pageType}")]
[EnableCors(origins: "*", headers: "*", methods: "options,get")]
public async Task<IHttpActionResult> GetFormInfo(string code, string pageType)
{
DateTime startTime = DateTime.Now;
if (string.IsNullOrEmpty(code))
return BadRequest("Invitation code is required.");
if (!IsValidPageType(pageType))
return BadRequest("Valid page types may be 'post', 'info', 'No Code' or blank.");
if (Request != null)
{
string startLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardStartEventNames.EVENT_TYPE_VISITOR_FORM_REQ_START);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, startLogMessage);
}
// *** Work happens here
RecipientModel visitorFormInformation = _repository.GetVisitorFormInformation(code, pageType);
// ***
if (visitorFormInformation == null)
return NotFound();
if (Request != null)
{
string endLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardEndEventNames.EVENT_TYPE_VISITOR_FORM_REQ_END, startTime);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, endLogMessage);
}
try
{
DateTime startLogActivityTime = DateTime.Now;
if (Request != null)
{
string startLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardStartEventNames.EVENT_TYPE_UPDATE_VISIT_LOG_FORM_VIEWED_START);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, startLogMessage);
}
// Log visitor activity, asynchronously
await _repository.LogActivity(visitorFormInformation.RecipientId, visitorFormInformation.IsCompleted);
if (Request != null)
{
string endLogMessage = CustomLogMessageBuilder.GetLogMessage(CustomLogMessageBuilder.StandardEndEventNames.EVENT_TYPE_UPDATE_VISIT_LOG_FORM_VIEWED_END, startLogActivityTime);
_tracer.Info(Request, this.ControllerContext.ControllerDescriptor.ControllerType.FullName, endLogMessage);
}
}
catch
{
return BadRequest("Recipient ID#" + visitorFormInformation.RecipientId.ToString() + " not found.");
}
return Ok(visitorFormInformation);
}
要查看我的自定义跟踪处理程序,请参阅上面发布的 Filip Wojcieszyn 的博客文章。我的版本几乎相同,因此为了简洁起见,我省略了它。
如果我在使用跟踪侦听器方面完全按照书上的规定进行操作,那么它会不会是我传递的内容?在当前状态下,只要禁用跟踪侦听器,该服务就可以工作。
在有人问之前,我已经在各种浏览器中试过了。但我在机智的帮助下,将不胜感激任何有用的评论和建议。谢谢!
问题是 HttpContext 在控制器的构造函数中还不可用,这就是 Request
属性 为 null 的原因,这导致 _tracer
为 null,这会导致对 _tracer
的任何函数调用抛出 ArgumentNullException
.
我建议您阅读更多有关 WebApi 管道的内容,例如 here。它告诉您控制器是如何构建的,以及当请求入站时在什么时候发生了什么。
简而言之:解决方案是不将 _tracer
存储在控制器级别,而是存储在正在执行的操作中,并在每次操作调用时获取 'new' 跟踪器。
public class VisitorFormController : ApiController
{
private IDataAccessRepository _repository;
public VisitorFormController(IDataAccessRepository repository): base()
{
_repository = repository;
}
[HttpGet]
[Route("api/VisitorForm/{code}/{pageType}")]
[EnableCors(origins: "*", headers: "*", methods: "options,get")]
public async Task<IHttpActionResult> GetFormInfo(string code, string pageType)
{
var tracer = Request.GetConfiguration().Services.GetTraceWriter();
// Use tracer from here on...
}
}