ASP NET MVC OnException not being called ApiController - c#

I am trying to intercept all exceptions, but the code is never run. I have tried putting this to GlobalFilters, and also putting it directly on my method.
My Attributes:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class HandleExceptionAttribute : HandleErrorAttribute
{
private ILog log = LogManager.GetLogger(typeof(HandleExceptionAttribute));
public override void OnException(ExceptionContext filterContext)
{
log.Info("inside on exception"); // this never appears
}
}
My class:
public class Tester
{
[HandleException]
public void Except()
{
var asd = 0;
var qwe = 1 / asd;
}
}
Dividing by zero give me an exception, my debugger catches it, I continue, but nothing is written into log file.
The logger works. Other logs appear in file. Even if I disable debugging, it doesn't read the log file, so it's not debuggers fault.
Running this on IIS Express. Windows 7.
EDIT:
Moved the thing to controller. Still not working
public class UserController : ApiController
{
private ILog log = LogManager.GetLogger(typeof(UserController));
[HandleException]
[CheckModelForNull]
[ValidateModelState]
public object Post([FromBody]User user)
{
var asd = 0;
var qwe = 1 / asd;
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
}

ApiControllers do not use HandleErrorAttribute
Should better use ExceptionFilterAttribute
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
log.error("ERROR",context.Exception);
}
}
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling

As noted in a comment, Filter attributes only apply to actions in controllers. If you want to also capture errors from other classes or something that happens before the code enters an action, you need to overwrite Application_Error method in Global.asax:
protected void Application_Error(object sender, EventArgs e)
{
log.Info("inside on exception");
}

Related

Is there any way to execute code once in a ASP.NET Web API REST service?

I have an ASP.NET Web API REST service and I would like to execute some code once when service is started only for first time, not each time a web api method is requested/invoked from my ASP.NET MVC application.
I would like to do this because I want to initialize an EventLog and then use it to create entries in the windows event viewer.
Is there some easy way to do it?
UPDATE:
As Jonhatan suggested in his answer I create a method within global.asax.cs:
Global.asax.cs:
namespace MyWebAPIApp
{
public class WebApiApplication : System.Web.HttpApplication
{
public MyLog _myLog;
protected void Application_Start()
{
// Here some stuff
SetupEventLogging();
}
private void SetupEventLogging()
{
if (!EventLog.SourceExists("MyWebApiLog"))
{
EventLog.CreateEventSource("MyWebApiLog", "MyWebApiLogLog");
}
EventLog eventLog = new EventLog();
eventLog.Source = "MyWebApiLog";
eventLog.Log = "MyWebApiLog";
_myLog = new MyLog(eventLog, "MyWebApiService");
}
}
}
Controller:
namespace MyWebAPIApp.Controllers
{
public class MyController : ApiController
{
public void GetAll()
{
_myLog.Success("All records read");
}
}
}
But now if I create a global variable _myLog, how can I access this variable from all the methods in my Controller in order to do _myLog.Error(...) or _myLog.Success(...)?
You would typically do that in the ApplicationStart method in your global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
SetupLogging(); // do something in here / wire up your flavour of logging
}
Often, the pattern will be:
set up your logging on app start - this is where you set up the database connection to store the logs, etc
call a static logger.Write method throughout your code whenever you want to write to log.
I use Microsoft.Practices.EnterpriseLibrary.Logging, but I think Serilog or Log4Net are 2 probably more common frameworks now.
So, in my global.asax.cs, the SetupLogging() method is:
private static void SetupLogging()
{
var configurationSource = ConfigurationSourceFactory.Create();
DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory(configurationSource));
var logWriterFactory = new LogWriterFactory(configurationSource);
Logger.SetLogWriter(logWriterFactory.Create());
var daysToKeepLogsInDb = int.Parse(ConfigurationManager.AppSettings["DaysToKeepLogsInDb"]);
CustomLogger.PurgeLogs(daysToKeepLogsInDb); // only keep last 90 etc days of event logging in the db
CustomLogger.Write("Application Starting", TraceEventType.Information);
}
Basically just the things that the framework needs to 'get going', and a little custom cleanup. And then I have a CustomLogger class to help write entries the way I want, run a custom stored procedure to clean up old logs, etc:
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
namespace MyApplication.Helpers
{
public class CustomLogger
{
private static readonly ICollection<string> EmptyCategoriesList = new List<string>(0);
private const string LogTitle = "MyApplication Name";
public static void Write(object message)
{
Write(message, TraceEventType.Error);
}
public static void Write(object message, TraceEventType severity)
{
Logger.Write(message, EmptyCategoriesList, -1, 1, severity, LogTitle);
}
public static void PurgeLogs(int keepLastXDays)
{
var connectionString = ConfigurationManager.ConnectionStrings["MyLoggingConnectionString"].ConnectionString;
using (var con = new SqlConnection(connectionString))
{
using (var command = new SqlCommand("PurgeLogs", con)) // custom stored procedure
{
var dateTo = DateTime.Now.AddDays(keepLastXDays * -1);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#dateTo", dateTo));
command.Parameters.Add(new SqlParameter("#title", LogTitle));
con.Open();
command.ExecuteNonQuery();
con.Close(); // technically not required because in using, but leaving in case this block gets copy-pasted out of here
}
}
}
}
}
And then, within my code (controller, helper, whatever), I write tot he log through the static method in the custom logger:
public static void EndSession(Session session)
{
try
{
Logon.DoLogoff(session);
}
catch (Exception exception)
{
CustomLogger.Write(exception);
throw new Exception("Error ending session.");
}
}
If you do this with dependency injection, it would (especially) allow you to swap out your logging framework more easily, and allow you to unit test a little more easily. But you would have to create another 'layer' between your application and the logger to abstract the relationship out a bit more. You should read up on dependency injection, as it is something that is often worth using.
But now if I create a global variable _myLog, how can I access this variable from all the methods in my Controller in order to do _myLog.Error(...) or _myLog.Success(...)?
Make _myLog static and reference it WebApiApplication._myLog where WebApiApplication is application class defined in global.asax.cs.
I'd rather create some static class with MyLog static property:
public static class LogManager
{
public static MyLog Logger;
}
And in global.asax.cs in SetupEventLogging() put
LogManager.Logger = new MyLog(eventLog, "MyWebApiService");

How to keep alive a variable among a class inherit from ApiController and other one inherit from ExceptionFilterAttribute

I have my controller SomeController and its inherited from ApiController, also, I have an ActionFilter:
FilterConfig
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
GlobalConfiguration.Configuration.Filters.Add(new LogExceptionFilterAttribute());
}
}
ErrorLogService
public static class ErrorLogService
{
public static void LogError(Exception ex, string metodo, string clase)
{
Utilidades.EnviarErrorHTTP(ex, null, metodo, clase);
}
}
LogExceptionFilterAttribute
public class LogExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//TODO
}
}
Well, the session is handled by the ApiController and in my SomeController I can use it like:
var session = TokenUser;
But, there nothing in my ErrorLogService to invoke the function to know the token.
Is there a way to share this variable if it is different in each session?
(TokenUser is an object).
I found a way to do it.
In the Global.asax you must add the next code:
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
And now, you are available to use Session:
var session = System.Web.HttpContext.Current.Session;
session["token"] = sesion;
And the variable session would persist in the application.

Can't write to response in Application_Start

In my Application_Start method I'm doing some setup and logging to my database. I had an issue where my connectionstring was wrong, which is not a big deal but I'd like to validate the database is available during Application_Start() and report back to the user if it's down.
Since the httpResponse isn't yet available I can't write something to the browser.
What other practical options do I have?
Here's a quick implementation of my suggestion.
Modify global.asax to have a public variable
public class MvcApplication : System.Web.HttpApplication
{
public static bool IsConfigured { get; set; }
Set IsConfigured = true as you leave Application_Start if everything is configured
Then add a ActionFilter like this
public class ConfiguredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (MvcApplication.IsConfigured) return;
filterContext.Result = new ViewResult
{
ViewName = "Offline",
TempData = filterContext.Controller.TempData
};
}
}
Create your Offline view in the Shared views folder
Register your new filter
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ConfiguredAttribute());
}
}

stop loading data after 15 min c#

I am writing an website where I get some data from the database. When starting the website on my computer I get the data for 15 min. After these 15 min the files don't load anymore.
When I restart the backend (Visual Studio C#) then it happens the same.
Controller from the file:
[UnitOfWorkActionFilter]
[RoutePrefix("categories")]
public class CategoriesController : ApiController {
private ICategoriesProcessor _categoriesProcessor;
private IPagedDataRequestFactory _pagedDataRequestFactory;
public CategoriesController(ICategoriesProcessor categoriesProcessor, IPagedDataRequestFactory pagedDataRequestFactory) {
_pagedDataRequestFactory = pagedDataRequestFactory;
_categoriesProcessor = categoriesProcessor;
}
[Route()]
[HttpGet]
public PagedResponse<Category> GetCategories(HttpRequestMessage requestMessage) {
var request = _pagedDataRequestFactory.Create(requestMessage.RequestUri);
return _categoriesProcessor.GetCategories(request);
}
}
here is the code from the UnitWorkActionFilterAttribute
public class UnitOfWorkActionFilterAttribute : ActionFilterAttribute {
public virtual IActionTransactionHelper ActionTransactionHelper { get { return WebContainerManager.Get<IActionTransactionHelper>(); } }
public override bool AllowMultiple { get { return false; } }
public override void OnActionExecuting(HttpActionContext actionContext) {
ActionTransactionHelper.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
ActionTransactionHelper.EndTransaction(actionExecutedContext);
ActionTransactionHelper.CloseSession();
}
}
I found out that the problem is, that the Session opens but not close but I don't know how to fix it.
Does someone has an idea why it's not working?
have you try access from Fiddler ??? what the client you used to call your API...
see what the fiddler got message, and if you call the API, that is call that Method API or not...need detail information, this error have come to method or just in client stuff...

UnitTest ApiController with ExceptionFilterAttribute

i'm trying to UnitTest my controller where an exception is catch by the ExceptionFilterAttribute and launched back as a HttpResponseException.
Controller
[ExceptionFilters] //ExceptionFilterAttribute
public class EleveController : ApiController
{
private IGpiRepository _gpiRepository;
public EleveController(IGpiRepository gpiRepository)
{
_gpiRepository = gpiRepository;
}
[HttpGet]
[Route("{fiche:int}/grouperepere")]
public GroupeRepere GroupeRepere(int fiche) //This What Im trying to test
{
GpiService service = new GpiService(_gpiRepository);
return service.Get(fiche); //Throw an ArgumentNullException when fiche == 0
}
}
ExceptionFilter
public class ExceptionFilters : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
else if (context.Exception is ArgumentNullException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(string.Format("Argument \"{0}\" is null or invalid", ((ArgumentNullException)context.Exception).ParamName)),
ReasonPhrase = "Argument null or invalid"
};
}
}
and this is my test:
private IGpiRepository _gpiRepository;
private Mock<ICallApi> _callApi;
private EleveController _controller;
[TestInitialize]
public void Initialize()
{
_callApi = new Mock<ICallApi>();
_gpiRepository = new GpiRepository(_callApi.Object);
_controller = new EleveController(_gpiRepository);
}
[TestMethod]
public void EleveController__GroupeRepere_WithBadFiche_400BadRequest()
{
string noGroupe = "111";
int fiche = 0;
try
{
GroupeRepere gp = _controller.GroupeRepere(fiche);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is HttpResponseException); // not working --> ArgumentNullException
}
}
The problem is that e still is an ArgumentNullException. When i go on debug, it doesn't even reach the ExceptionFilter class
Am i missing something?
Thanks.
Your test is directly against the controller. ExceptionFilterAttribute depends on a server.(remember: attributes are Metadata)
The way to test the behavior is to use IIS server or SelfHost Server, then raise the server in your test class and send the request:
[TestInitialize]
public void Initialize()
{
_callApi = new Mock<ICallApi>();
_gpiRepository = new GpiRepository(_callApi.Object);
//initialize your server
//set _gpiRepository as a dependency and etc..
}
[TestMethod]
public void EleveController__GroupeRepere_WithBadFiche_400BadRequest()
{
//configure the request
var result = client.ExecuteAsGet<GroupeRepere>(<your request>);
Assert.AreEqual(HttpStatusCode.BadRequest,result.StatusCode);
}
In my opinion you shouldn't error code unless your controller is apart of public Api.(the reason is simple this kind of tests are very simple to break, thay are slow and thay use expensive resources) if your controller is a part
public Api you should test it through your Acceptance tests, then you verify that nothing override the expected behavior.
If you still want to test this behavior
then i'd like to offer you an alternative way to test it:
Create UT against ExceptionFilters.
Create a UT which verifies that the method has ExceptionFilters attribute
For example:
[TestMethod]
public void GroupeRepere_HasExceptionFiltersAttribute()
{
var attribute = typeof (UnitTest2).GetMethod("GroupeRepere").GetCustomAttributes(true);
foreach (var att in attribute)
{
if(att.GetType() is typeof(ExceptionFilters))
{
return;
}
}
Assert.Fail();
}
Pros:
it' fast, not so easy to break, it doesn't use expensive reasorces.
Cons:
In production some setting could override the expected behavior.

Categories

Resources