Global Error Handling in MVC 6 - c#

In my MVC 5 application unhandled exception are capture within the Global Application_Error event and then redirected to ErrorController.
The last error is added to HttpApplicationState in the Global.Error event and retrieved in the Controller.
MVC 6 doesn't have the Global file. it uses the IExceptionFilter in capturing the unhandled exception.
public class GlobalExceptionFilter : IExceptionFilter, IDisposable {
private readonly ILogger logger;
private bool _disposed;
public GlobalExceptionFilter(ILoggerFactory logger) {
if (logger == null) {
throw new ArgumentNullException(nameof(logger));
}
this.logger = logger.CreateLogger("Global Exception Filter");
}
public void OnException(ExceptionContext context) {
logger.LogError("GlobalExceptionFilter", context.Exception);
//redirect to controller
}
public void Dispose() {
if (this._disposed) {
return;
}
this._disposed = true;
}
private static int GetHttpStatusCode(Exception ex) {
if (ex is HttpResponseException) {
return (int)(ex as HttpResponseException).HttpStatusCode;
}
return (int)HttpStatusCode.InternalServerError;
}
}
Is it possible to do the same in OnException?

From asp.net core documentation (check here)
In general, filters are meant to handle cross-cutting business and
application concerns. This is often the same use case for middleware.
Filters are very similar to middleware in capability, but let you
scope that behavior and insert it into a location in your app where it
makes sense, such as before a view, or after model binding. Filters
are a part of MVC, and have access to its context and constructs. For
instance, middleware can’t easily detect whether model validation on a
request has generated errors, and respond accordingly, but a filter
can easily do so.
Based on explanation of this documentation. Both middle ware approach and filter approach will meet your requirements. If you need more information of MVC pipeline and its information of errors, you should use filter approach.

Related

Implementing Airbrake into a ASP.NET MVC 5 project

I have an older .NET 4.8 project that needs to use Airbrake. The project is using Unity for its IoC container, implementing the standard Repository Service pattern.
There's very little in the way of ASP.NET examples.
I am looking to do something like this:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType(typeof(ILogger<>), typeof(ILogger<>));
container.RegisterType<IMyService, MyService();
}
public class MyController
{
private readonly ILogger<MyController> _logger;
private readonly IMyService _myService;
public MyController(ILogger<MyController> logger, IMyService _myService)
{
_logger = logger;
_myService = myService;
}
public MyMethod()
{
try
{
var x = _myService.DoThis();
}
catch (Exception ex)
{
_logger.LogError(e, e.Message);
}
}
}
I believe I need to either somehow register Airbrake with ILogger or perhaps create my own logging service.
public class Logging : ILogging
{
public void LogError(Exception e, string message)
{
var airbrake = new AirbrakeNotifier(new AirbrakeConfig
{
ProjectId = // pulled from web.config somehow
ProjectKey = // pulled from web.config somehow
});
var notice = airbrake.BuildNotice(ex);
airbrake.NotifyAsync(notice).Result;
}
}
I have tried using this as starting point: https://github.com/airbrake/sharpbrake/blob/master/docs/asp-net-http-module.md
This is excellent, but I need to extend it somehow to be able to use it within my services and not just the .Web project.
I know there's the ASP.NET module that will automatically capture the errors but I am wanting to manually log when I see fit, and avoid having to call the airbrake client every time I want to log an error.
Is there a way to do this or am I completely misunderstanding how this should be working?
You don't actually need to wire it up as part of the .NET ILogger. I am sure there is a way (probably via OWIN) but you nothing stops you from writing a basic logging service as you would any other service and using that via bog standard DI. The answer was pretty much in the question to begin with.

Log all handles exception

How can I log all handled exceptions?
I want that whenever I catch an exception I should be able to log it
I want it to work globally and not that i should have to write it each time I catch
I tried subscribing to AppDomain.CurrentDomain.FirstChanceException and it did work but I did not have the full stack trace and it called multiple times for each exception (I don't know why)
I also tried wrapping my controller with ActionFilterAttribute like below and it worked on all exception from the controller only and not if the exception was caught in a service that was called from the controller
public class ExceptionLoggingHandler : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if(filterContext.Exception !=null)
{
System.Diagnostics.Debug.WriteLine(filterContext.Exception.Message);
}
base.OnResultExecuted(filterContext);
}
}
In ASP.NET MVC, you can add your filter as a global filter in the RegisterGlobalFilters method inside FilterConfig.cs. It should then catch all exceptions in all controller actions, and in any methods called from those actions - unless of course those methods already have catch blocks inside them which swallow the exception. In that case the caught exception (unless it's then re-thrown) will inevitably go undetected higher up the stack, which is, naturally, the whole point of catching exceptions.
e.g.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new ExceptionLoggingHandler());
}
}
Also, your attribute should inherit from HandleErrorAttribute, not ActionFilterAttribute.
Something like this:
public class ExceptionLoggingHandler : HandleErrorAttribute
{
public ExceptionLoggingHandler() : base()
{
}
public override void OnException(ExceptionContext context)
{
System.Diagnostics.Debug.WriteLine(context.Exception.Message);
context.ExceptionHandled = true;
//.... continue to produce a suitable response
}
}
(In the .... area you can continue to develop the handler to log more sophisticated data, and return a suitable response, perhaps along the lines of this one (other examples are also available online.)

Custom error handling globally in asp.net web api

I am using asp.net web api.
[Route("api/employee")]
[HttpPost]
public dynamic GetData(EmployeeModel model)
{
EmployeeService emp = new EmployeeService();
emp.GetData(model);
}
This is how I am handling error globally:
public class ExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//Code to log the exception goes here:
}
}
in the WebApiConfig.cs file, I am registering the filter:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
....
config.Filters.Add(new ExceptionFilter());
}
}
Whenever there is an exception, the exception filter is able to catch the exception & It can log the exception. All this is working fine.
What I want: With every exception, I want to log specific details, those details are available in respective methods but how do I pass them to exception filter? The only work around I see is, add try catch block in individual methods and log the exception along with specific details.
Another thing that I want to capture is the request object of each request. In this case it will be ExployeeModel. Even if I somehow get the request object, how to I type cast into correct type. One endpoint can expect EmployeeModel other can expect DepartmentModel.

MVC 6 HttpResponseException

I am trying to return status codes in the following way
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "invalid username/password" });
now this is not supported in MVC6, which sucks because using IActionResult seems really silly to me, and way less intuitive.
I have found the following posts one and two.
the first leads to a broken link, and the second applies to an MVC application.
I did realize that I need to create a middleware to address this issue, but I am not sure where to start, and since this is pretty useful stuff, I would expect there to be some open source solution I could use or maybe a code snippet.
For the record, I am using ASP.net 5 rc1 update 1 MVC 6
I think this change is due to .NET Core - where ASP.NET tries to do everything out of the box, ASP.NET Core only does what you specifically tell it to (which is a big part of why it's so much quicker and portable).
If you want this behaviour in Core you need to add it, either as a package that someone has written for you or by rolling your own.
It's fairly simple. First you need a custom exception to check for:
public class StatusCodeException : Exception
{
public StatusCodeException(HttpStatusCode statusCode)
{
StatusCode = statusCode;
}
public HttpStatusCode StatusCode { get; set; }
}
Then you need a RequestDelegate handler that checks for the new exception and converts it to the HTTP response status code:
public class StatusCodeExceptionHandler
{
private readonly RequestDelegate request;
public StatusCodeExceptionHandler(RequestDelegate next)
{
this.request = next;
}
public Task Invoke(HttpContext context) => this.InvokeAsync(context); // Stops VS from nagging about async method without ...Async suffix.
async Task InvokeAsync(HttpContext context)
{
try
{
await this.request(context);
}
catch (StatusCodeException exception)
{
context.Response.StatusCode = (int)exception.StatusCode;
context.Response.Headers.Clear();
}
}
}
Then you register this middleware in your startup:
public class Startup
{
...
public void Configure(IApplicationBuilder app)
{
...
app.UseMiddleware<StatusCodeExceptionHandler>();
Finally you can throw the HTTP status code exception in your actions, while still returning an explicit type that you can easily unit test:
public Thing Get(int id) {
Thing thing = GetThingFromDB();
if (thing == null)
throw new StatusCodeException(HttpStatusCode.NotFound);
return thing;
}
This is fairly simple and someone out there has probably written a more complete one, but I haven't been able to find one or get a clear answer as to why this was dropped.

Logging Visual Studio MVC5

I am working on a project that is a web application. The application should have a single button on the page that redirects the user back to the same page. In 1 of every 5 (or thereabout) occasions when you press the button the program should throw some exception and log it.
It should catch the exception and do three things: Send an email, write the error in a file and write it to debug window.
My controller so far:
public class HomeController : Controller
{
public ActionResult Index()
{
try
{
RedirectToAction("Index");
}
catch (ArgumentException e)
{
}
Random RandNumber = new Random();
int rand = RandNumber.Next(1000);
if(rand % 5 == 0)
{
throw new System.ArgumentException("This is a random excpetion");
}
return View();
}
}
The idea is the to have the class Logger that declares a collection of the class LogMedia and loops through all the instances.
Class Logger:
public class Logger
{
List<LogMedia> m_loggers = new List<LogMedia>();
void LogException(Exception ex)
{
foreach(var i in m_loggers)
{
//Loop through all Log Media instances
}
}
Class LogMedia
class LogMedia
{
public virtual void LogMessage(string Message); //virtual function that doesen't do anything
}
class OutputWindowLogMedia:LogMedia
{
public override void LogMessage(string Message)
{
System.Diagnostics.Debug.WriteLine(Message);
}
}
class TextFileLogMedia : LogMedia
{
public override void LogMessage(string Message)
{
File.AppendAllText("c:\\Temp\\Log.txt", Message);
}
}
class EmailLogMedia : LogMedia
{
public override void LogMessage(string Message)
{
//send email
}
}
}
My questions to you are the following:
Will my controller work as it stands now with what I am trying to implement? I am especially skeptical of my try catch..
What is the best way to loop though these three instances?
Do I have the right class declerations for Log Media, i.e. should I create new classes(that inherits LogMedia) for each instance and then a function that overrides the virtual function in LogMedia.
It's unclear if you want this behavior on this particular action, the controller, or the entire app. That said, unless there is some specific recovery code you want to build into your logic, I wouldn't pollute my action code with the try-catch.
There are two options the MVC framework provides to handle errors:
First, you can override OnException in a specific controller:
protected override void OnException(ExceptionContext filterContext)
{
// do your logging here
// set this flag if you want to stop the exception from bubbling
filterContext.ExceptionHandled = true;
}
Second, you can create an error handling filter:
public class MyExceptionFilterAttribute :
System.Web.Mvc.FilterAttribute,
System.Web.Mvc.IExceptionFilter
{
public void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
// same error handling logic as the controller override
}
}
A filter can either be added to the global filters list, or applied to an action method like this:
[MyExceptionFilter]
public ActionResult Index()
{
}
Edit: forgot to mention your logger structure. Your approach of having Logger loop over multiple instances of LogMedia is a good, common approach to supporting multiple logging mechanisms with one common interface (log4net appenders for example). That said, have you considered using an existing, proven framework for your logging needs? You get a thoroughly tested framework to run, and a skill that will carry over to future endeavours.
Edit2: after your comment, I took a closer look at your code instead of your exception trapping approach. In your Index action, you're redirecting to Index with no condition checking. This is going to end up as a constant redirect loop (until IIS stops you). Since this is an assignment, I don't want to give too much away, but you will need some logic on the server side to detect a button click and redirect back to your Index. Consider another Index action method that accepts HttpPost (your current action would be the HttpGet handler).

Categories

Resources