'.NET CORE, how to get file location for an exception in a web app, in middleware?
I've seen this question:
How can I get the line number and file name from an exception in net Core?
And tried the code, but it always says line 0 and no file name is supplied when called in middleware for all the frames in the exception.
I have an unhandled exception middleware I have written and the main part looks like this:
public async Task Invoke(HttpContext context)
{
var num = 0;
Exception exceptionThatOccurred = null;
try
{
await _next(context);
}
catch (Exception ex)
{
if (context.Response.StatusCode != (int) HttpStatusCode.OK)
{
await _next(context);
return;
}
num = 1;
exceptionThatOccurred = ex;
}
if (num == 1)
{
//do stuff to process the error
Everything works fine, any unhandled exception processes the error. But in trying to get the file name, method, and line numbers inside that:
var trace = new StackTrace(exceptionThatOccurred, true);
var frames = trace.GetFrames();
var frameDataMessages = new List<string>();
foreach (var frame in frames)
{
var lineNumber = frame.GetFileLineNumber();
var fileName = frame.GetFileName();
var methodName = frame.GetMethod();
var errorFileLocatonMessage = "<strong>" + fileName + "</strong> on <strong>line " + lineNumber + "</strong> in method <strong>" + methodName + "</strong>.";
frameDataMessages.Add(errorFileLocatonMessage);
}
I get an output like this:
on line 0 in method Int32 SaveChanges().
on line 0 in method Void Commit().
on line 0 in method Void Commit().
on line 0 in method Void Save(TClass, System.String, Boolean, Boolean).
on line 0 in method Microsoft.AspNetCore.Mvc.IActionResult Index().
on line 0 in method System.Object lambda_method(System.Runtime.CompilerServices.Closure, > System.Object, System.Object[]).
on line 0 in method Void MoveNext().
on line 0 in method Void Throw().
on line 0 in method Void MoveNext().
on line 0 in method Void Throw().
on line 0 in method Void Rethrow(Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext).
on line 0 in method System.Threading.Tasks.Task Next(State ByRef, Scope ByRef, System.Object ByRef, Boolean ByRef).
on line 0 in method Void MoveNext().
on line 0 in method Void Throw(). on line 0 in method Void Rethrow(Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext).
on line 0 in method System.Threading.Tasks.Task Next(State ByRef, Scope ByRef, System.Object ByRef, Boolean ByRef).
on line 0 in method Void MoveNext().
on line 0 in method Void Throw().
on line 0 in method VoidHandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task).
on line 0 in method Void GetResult().
on line 0 in method Void MoveNext().
Now it looks like I should only be getting the first frame where the relevant info is, and the stack trace shows something like this:
at MyFramework.Repositories.MyDbContext.SaveChanges() at MyFramework.Repositories.EntityFrameworkQueryable'4.Commit() at MyFramework.Repositories.EntityRepository'3.Commit() at MyFramework.Services.EntityService'2.Save(TClass entity, String action) at MyWebsite.Controllers.TestsController.Index() at lambda_method(Closure , Object , Object[] ) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__27.MoveNext() --- End of stack trace from previous location where exception was thrown ---
The top level line where the exception is thrown is:
var test = new Equipment();
test.Id = Guid.Empty;
_equipmentService.Save(test, "Testing errors");
In TestsController.cs, line 60, method Index. But how can I get that info out of the frame data?
Solution 1:[1]
UPDATE
I use below Global Error Handler to capture the exception, please try it. And this is a sample code, the classification of Exception is not very comprehensive, you can further optimize it if you need it.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using webapi.Helpers;
namespace webapi.Middleware
{
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleException(context, ex);
}
finally {
await Handle400Exception(context);
}
}
private static Task HandleException(HttpContext context, Exception ex)
{
var response = context.Response;
response.ContentType = "application/json";
string DetailedMsg = string.Empty;
switch (ex)
{
case AppException e:
response.StatusCode = (int)HttpStatusCode.BadRequest;
break;
case KeyNotFoundException e:
response.StatusCode = (int)HttpStatusCode.NotFound;
break;
case System.AggregateException e:
response.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
case FileNotFoundException e:
response.StatusCode = (int)HttpStatusCode.InternalServerError;
DetailedMsg = AnalyzeException(e);
break;
default:
response.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
var result = JsonSerializer.Serialize(new { message = ex?.Message, detailedMsg= DetailedMsg.Equals(string.Empty)?"Nothing":DetailedMsg });
return context.Response.WriteAsync("Status Code: 500; Internal Server Error, Message = " + result);
}
private static Task Handle400Exception(HttpContext context)
{
var response = context.Response;
response.ContentType = "application/json";
string Message = string.Empty;
if (response.StatusCode == 401) {
Message = "unauthorize";
}
if (response.StatusCode == 404)
{
Message = "Not found, pls check url";
}
var result = JsonSerializer.Serialize(new { message = Message });
return context.Response.WriteAsync("Status Code: 400; Message = " + result);
}
private static string AnalyzeException(Exception e)
{
var trace = new StackTrace(e, true);
var stackFrame = trace.GetFrames()[trace.FrameCount - 1];
var reflectedType = trace.GetFrames()[trace.FrameCount - 1].GetMethod().ReflectedType;
var additionalInformation = new AdditionalInformation();
if (reflectedType != null)
{
additionalInformation = new AdditionalInformation()
{
Column = stackFrame.GetFileColumnNumber(),
Line = stackFrame.GetFileLineNumber(),
MethodName = reflectedType.FullName,
File = stackFrame.GetFileName()
};
}
return "";
}
public class AdditionalInformation {
public int Column { get; set; }
public int Line { get; set; }
public string MethodName { get; set; }
public string File { get; set; }
}
}
}
PREVIOUS
Try below code and it works for me. If you still want use GetFrames(), you can refer below code.
var stackFrame = trace.GetFrames()[trace.FrameCount - 1];
var reflectedType = trace.GetFrames()[trace.FrameCount - 1].GetMethod().ReflectedType;
Test Result
My Test Code
private static string AnalyzeException(Exception e)
{
var trace = new StackTrace(e, true);
var stackFrame = trace.GetFrame(trace.FrameCount-1);
var reflectedType = trace.GetFrame(trace.FrameCount-1).GetMethod().ReflectedType;
var additionalInformation = new AdditionalInformation();
if (reflectedType != null)
{
additionalInformation = new AdditionalInformation()
{
Column = stackFrame.GetFileColumnNumber(),
Line = stackFrame.GetFileLineNumber(),
MethodName = reflectedType.FullName,
File = stackFrame.GetFileName()
};
}
return "";
}
public class AdditionalInformation {
public int Column { get; set; }
public int Line { get; set; }
public string MethodName { get; set; }
public string File { get; set; }
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |