I'm creating an attribute so that whenever an exception occurs on my site, I'll receive an email detailing the exception. I've got so far but my Attribute code doesn't seem to fire if an exception occurs:
public class ReportingAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
// This will generate an email to me
ErrorReporting.GenerateEmail(filterContext.Exception);
}
}
Then above my Controller I'm doing:
[ReportingAttribute]
public class AccountController : Controller
The other way to do it is ofcourse putting ErrorReporting.GenerateEmail(ex) inside my catch blocks? There must be a simpler way? Thats why I thought of creating the Attribute to handle this
For the purpose of logging all uncaught exceptions, you can define the following method in your Global.asax.cs file:
private void Application_Error(object sender, EventArgs e)
{
try
{
//
// Try to be as "defensive" as possible, to ensure gathering of max. amount of info.
//
HttpApplication app = (HttpApplication) sender;
if(null != app.Context)
{
HttpContext context = app.Context;
if(null != context.AllErrors)
{
foreach(Exception ex in context.AllErrors)
{
// Log the **ex** or send it via mail.
}
}
context.ClearError();
context.Server.Transfer("~/YourErrorPage");
}
}
catch
{
HttpContext.Current.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
Attribute just by itself can not define a behaviour, but its used for make some marks over your code data. You should write a code, where you
get an exception
check for given attribute presence in the method that raised an exception
if it is present, collect and send the data you need.
Why not create an base controller:
public ApplicationBaseController : Controller
{
public override void OnException(ExceptionContext context)
{
//Send your e-mail
}
}
And derive your controller from ApplicationBaseController
public HomeController : ApplicationBaseController
{
//.....
}
Related
Forgive me for my ignorance but at the moment I'm struggling to figure out the best approach to catch an exception and display a message to the client based on an exception type.
My architecture:
Repository
Page IPageRepository.FindDefault()
{
try
{
return MapPageFromCategory.MapFromEntity(_context.tbl_Category.
FirstOrDefault(p =>
p.IsLandingPage == true
)
);
}
catch (Exception ex)
{
throw new ApplicationException("Error getting values from database :", ex);
}
}
With the above as you can see it just fetches data from the DB, but in some scenarios I could get a object not set to an instance of an object exception due to physically no data in the table or a Null exception depending on the data passed in.
Now in both scenario I would like to generate a 404 exception which passes to my controller.
Controller
public class PageController : ApiController
{
private IPageRepository _pageRepository;
public PageController(IPageRepository pageRepository)
{
this._pageRepository = pageRepository;
}
// GET
[HttpGet]
[Route("")]
public Page Get()
{
return this._pageRepository.FindDefault();
}
}
My Controller hit's the method and if any of those exceptions are hit, what would be the best approach to intercept those exceptions and pass to the end client (Calling app) ?
Again apologies for the question, really trying to create some sort of systematic approach to exception handling.
Thanks for your time!
You can either have a separate try/catch in the Controller class and handle the exception thrown from repository there or do something different.
You can create a class similiar to this:
public class Result
{
public bool Succeded { get; private set; }
protected Result(bool succeeded)
{
Succeded = succeeded;
}
}
public class Result<TData> : Result
{
public TData Data { get; private set; }
protected Result(bool succeeded, TData data) : base(succeeded)
{
Data = data;
}
}
Then, you can return this from your repository and check in the controller if the call has succeeded. If not, throw a desired exception, which will set StatusCode accordingly.
I am coding an MVC 5 internet application, and I have a question in regards to handling exceptions globally.
I have my Application_Error setup in my global.asax file. This caters to errors such as 404 HttpExceptions.
How can I send all errors that occur in a controller to the Application_Error function? An example is the following exception:
System.Web.HttpRequestValidationException: A potentially dangerous
Request.Form value was detected from the client (name="").
I have written a OnException(ExceptionContext filterContext) for my controller, but am not sure on how to get the Application_Error function to handle these errors. Do I need to pass the exception from the OnException function, or is this the wrong approach?
Thanks in advance.
You can create a global filter by adding the following class to your App_Start folder:-
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
HandleErrorAttribute can be replaced with your own custom Exception Filter.
All you then need to do is make sure you add the following line of code to the App_Start method of your Gloabal.asax :-
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//AreaRegistration.RegisterAllAreas();
//RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}
}
Hope this helps.
I'm using some kind of http-module which gives me exactly what you are asking for:
public class MyModule : IHttpModule {
public void Init(HttpApplication context) {
context.Error += OnRequestError;
}
private void OnRequestError(object sender, EventArgs e) {
var context = ((HttpApplication)sender).Context;
var error = context.Error;
if (error == null)
return;
var errorType = error.GetType();
if (errorType == typeof(HttpException))
// do something
// this is what you are looking for
if (errorType = typeof(HttpRequestValidationException))
// do something, whatever you want
// works for me, so should work to you too
}
}
To get the module to work, you can use web.config or DynamicModuleHelper:
Install Microsoft.Web.Infrastructure and WebActivatorEx via nuget
Add a Bootstrapper class to your project
Register module at PreApplicationStartMethod
Sample:
// File: Bootstrapper.cs (contains class Bootstrapper)
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using WebActivatorEx;
using WhatEver.It.Is;
[assembly: PreApplicationStartMethod(typeof(Bootstrapper), "Bootstrap")]
namespace WhatEver.It.Is {
public class Bootstrapper {
public static void Bootstrap() {
// Do what do you need just before the application get started
// like registering modules, etc...
DynamicModuleUtility.RegisterModule(typeof(MyModule));
}
}
}
As described in the linked posts, I am not satisfied with the built in AuthorizeAttribute, because it always redirects to the declared login-page. I need different behaviour between 401-NotAuthenticated (I know it is called Unauthorized) and 403-Forbidden. Forbidden shouldn't link to the login-page.
As in this or this solutions suggested, I implemented a custom attribute called AuthenticateAndAuthorizeAttribute inheriting from AuthorizeAttribute.
public class AuthenticateAndAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext) {
if (filterContext.HttpContext.Request.IsAuthenticated) {
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
else {
base.HandleUnauthorizedRequest(filterContext);
}
}
}
All in all it does what it is supposed to do. But, because only the HttpStatusCode of the response is changed, I get an empty page. I know it is because there is no custom error page defined in the web.config. Instead we use the solution with "overriding" the method protected void Application_Error(object sender, EventArgs e) in the global.asax. This enables us, to create different error pages for default requests and ajax requests.
Unfortunatly this does not work when just setting the status code of the response. So I implemented the attribute, with throw HttpExceptions.
public class AuthenticateAndAuthorizeAttribute : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
if (filterContext.HttpContext.Request.IsAuthenticated) {
throw new HttpException((int)System.Net.HttpStatusCode.Forbidden, Forbidden!");
} else {
throw new HttpException((int)System.Net.HttpStatusCode.Unauthorized, "Not Authenticated!");
}
}
}
Now I wonder, if this is a common/correct way to deal with authentication and authorization or if I break some built-in (security)features of asp.net? Could someone see any problems with this solution?
I have a simple controller :
public class UsersController : ApiController
{
[HttpPost]
[AllowAnonymous]
public HttpResponseMessage Login([FromBody] UserLogin userLogin)
{
var userId = UserCleaner.Login(userLogin.MasterEntity, userLogin.UserName, userLogin.Password, userLogin.Ua);
if (userId == null) return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "User not authorized");
return Request.CreateResponse(HttpStatusCode.OK, Functions.RequestSet(userId));
}
}
As you can see , only POST is currently available .
But when I invoke a GET in a browser (just for checking):
http://royipc.com:88/api/users
I get :
{"Message":"The requested resource does not support http method
'GET'."}
It is clear to me why it happens. But I want to return a custom exception when it happens.
Other answers here at SO doesn't show how I can treat this kind of situation (not that i've found of, anyway)
Question
How (and where) should I catch this kind of situation and return custom exception (HttpResponseMessage) ?
NB
I don't want to add a dummy GET method just for "catch and throw". tomorrow there can be a GET method. I just want to catch this Exception and return my OWN !
You may need to inherit from ApiControllerActionSelector class which is what the Web API uses to select the required action.
then you can replace the default IHttpActionSelector by your new action selector like that. config.Services.Replace(typeof(IHttpActionSelector), new MyActionSelector());
check this url for full example: http://www.strathweb.com/2013/01/magical-web-api-action-selector-http-verb-and-action-name-dispatching-in-a-single-controller/
You can build custom Exception filters in ASP.Net WebAPI. An exception filter is a class that implements the IExceptionFilter interface. To create a custom exception filter you can either implement the IExceptionFilter interface yourself or create a class that inherits from the inbuilt ExceptionFilterAttribute class. In the later approach all you need to do is override the OnException() method and plug-in some custom implementation.
public class MyExceptionFilter:ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An unhandled exception was thrown by the Web API controller."),
ReasonPhrase = "An unhandled exception was thrown by the Web API controller."
};
context.Response = msg;
}
}
you would likely want to test for conditions and generate the exact exception, but this is a bare example.
To use the exception class, you can either register it in the Global.asax, or as an attribute on a specific class or method.
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new WebAPIExceptionsDemo.MyExceptionFilter());
AreaRegistration.RegisterAllAreas();
...
}
}
or
[MyExceptionFilter]
public class UsersController : ApiController
{
...
}
Is there a better way to catch exceptions? I seem to be duplicating a lot of code. Basically in every controller I have a catch statement which does this:
try
{
Do something that might throw exceptions.
}
catch (exception ex)
{
Open database connection
Save exception details.
If connection cannot be made to the database save exception in a text file.
}
I have 4 controllers and around 5-6 actions methods in each controller which is a lot of code duplication. How can I trim down on the amount of line in the try catch statement above?
You could make use of Extension methods here.
Create an extension method in a new class.
public static class ExtensionMethods
{
public static void Log(this Exception obj)
{
// log your Exception here.
}
}
And use it like:
try
{
}
catch (Exception obj)
{
obj.Log();
}
You don't need to put try/catch blocks on every method. That's tedious and painful! Instead you can use the Application_Error event of Global.asax for logging the exceptions. The code below is the sample implementation which can be used to catch exceptions that occur in your web application.
protected void Application_Error(object sender, EventArgs e)
{
var error = Server.GetLastError();
if (!string.IsNullOrWhiteSpace(error.Message))
{
//do whatever you want if exception occurs
Context.ClearError();
}
}
I would like also to stress that "Handled exception" especially trying to put try/catch blocks on most methods is one of the "Top 3 silent performance killers for IIS / ASP.NET apps" as explained in this blog http://mvolo.com/fix-the-3-high-cpu-performance-problems-for-iis-aspnet-apps/
What you are trying to do is called a cross-cutting concern. You are trying to log any error that happens anywhere in your code.
In ASP.NET MVC cross-cutting concerns can be achieved by using Filters. Filters are attributes that can be applied globally, to a controller or to a method. They run before an action method executes or after it.
You have several types of filters:
Authorization filters, they run to check if the user is allowed to access a resource.
Action filters, these run before and after an action method executes.
Result filters, these can be used to change the result of an action method (for example, add some extra HTMl to the output)
Exception filters run whenever an exception is thrown.
In your case, you are looking for exception filters. Those filters only run when an exception happens in in an action method. You could apply the filter globally so it will automatically run for all exceptions in any controller. You can also use it specifically on certain controllers or methods.
Here in the MSDN documentation you can find how to implement your own filters.
Personally, since I greatly dislike try/catch blocks, I use a static Try class that contains methods that wrap actions in reusable try/catch blocks. Ex:
public static class Try {
bool TryAction(Action pAction) {
try {
pAction();
return true;
} catch (Exception exception) {
PostException(exception);
return false;
}
}
bool TryQuietly(Action pAction) {
try {
pAction();
return true;
} catch (Exception exception) {
PostExceptionQuietly(exception);
return false;
}
}
bool TrySilently(Action pAction) {
try {
pAction();
return true;
} catch { return false; }
}
// etc... (lots of possibilities depending on your needs)
}
I have used a special class in my applications that is called ExceptionHandler, in the class that is static I have some methods to handle application's exceptions. It gives me an opportunity to centralize exception handling.
public static class ExceptionHandler
{
public static void Handle(Exception ex, bool rethrow = false) {...}
....
}
In the method you can log the exception, rethrow it, replace it with another kind of exception, etc.
I use it in a try/catch like this
try
{
//Do something that might throw exceptions.
}
catch (exception ex)
{
ExceptionHandler.Handle(ex);
}
As Wouter de Kort has rightly stated in his answer, it is cross-cutting concern, so I've put the class in my Application Layer and have used it as a Service. If you defined the class as an interface you would be able to have different implementations of it in different scenarios.
Also you can use Singleton pattern:
sealed class Logger
{
public static readonly Logger Instance = new Logger();
some overloaded methods to log difference type of objects like exceptions
public void Log(Exception ex) {}
...
}
And
Try
{
}
Catch(Exception ex)
{
Logger.Instance.Log(ex);
}
Edit
Some peoples don't like Singleton for reasonable grounds.instead of singleton we can use some DI:
class Controller
{
private ILogger logger;
public Controller(ILogger logger)
{
this.logger = logger;
}
}
And use some DI library that will inject one instance of ILogger into your controllers.
I like the answers suggesting general solutions, however I would like to point out another one which works for MVC.
If you have a common controller base (wich you should anyways, it's a Best Practice IMO). You can simply override the OnException method:
public class MyControllerBase : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
DoSomeSmartStuffWithException(filterContext.Exception);
base.OnException(filterContext);
}
}
Then simply inherit your normal controllers from your common base instead of Controller
public class MyNormalController : MyControllerBase
{
...
If you like this you can check out the Controller class for other handy virtual methods, it has many.
In ASP .NET MVC you can implement your own HandleErrorAttribute to catch all the exceptions that occur in all controllers:
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
var ex = filterContext.Exception;
// Open database connection
// Save exception details.
// If connection cannot be made to the database save exception in a text file.
}
}
Then register this filter:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
}
}
And of-course call the register method on application start-up:
public class MvcApplication : HttpApplication
{
protected override void OnApplicationStarted()
{
// ...
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
// ...
}
}
Wouter de Kort has already explained the concept behind this in his answer.