we have an MVC application , what is the best way to record errors generated (by users) back to our support database? users should only see "this error has been reported" error message
all the details should be written to to a database
please help!
I suggest you either make use of External library like Log4Net or create you own library class which will do logging for you.
One more suggestion is make use of ExceptionFilter which is avaiable in MVC file and write code of logging in that filter. I am suggesting becuase it will not cause you to write code again and again it follows DRY principle.
Example :
public class CustomExceptionFilter: FilterAttribute,
IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
//log error here
Logger.LogException(filterContext.Exception);
//this will redirect to error page and show use logging is done
filterContext.Result = new
RedirectResult("customErrorPage.html");
filterContext.ExceptionHandled = true;
}
}
}
than you can do like this
//Over controller
[CustomExceptionFilter]
public class HomeController:Controller
{
//......
}
//Over the Action
[CustomExceptionFilter]
public ActionResult Index()
{
//.......
}
Check this for log4net : log4net Tutorial
Related
I'm just getting started with Asp.Net Core (and asp.net in general) and I'm trying to build nice controller classes for my rest api.
I'm trying to inherit from a base controller to avoid redefining routes and logic such as validation for resources like so (non working example):
[Route("/api/v1/users/{id}")]
public class UserController: Controller
{
protected int userId;
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
// Validate userId..
userId = (int) RouteData.Values["id"];
}
[HttpGet]
public IActionResult Info()
{
// Use this.userId here
return this.Json("User info..");
}
}
[Route("/friends")]
public class UserFriendsController: UserController
{
[HttpGet]
public IActionResult Info()
{
// Use this.userId here
return this.Json("List of user's friends..");
}
}
I realize I can put this into a single class with multiple actions, but my real scenario involves more controllers that all may want to inherit from UserController.
Route attributes cannot be inherited.
You can play with Routing Middleware. See documentation and the examples on Routing github repo.
Also I can recommend this ASP.NET Core 1.0 - Routing - Under the hood article, as it has good explanation about how routing works:
I'm trying to inherit from a base controller to avoid redefining
routes and logic such as validation for resources
If you want to check whether current user has access right on the resource or not, you should use Resource Based Authorization. For other cross cutting concerns, you can use Filters or Middlewares.
I am working on a MVC application and I have a requirement of dealing with errors and session timeouts by redirecting the user to different error pages based on few parameters in the query string.
The issue I am facing is that i tried to implement this by saving the required parameters from querystring into a session and then redirecting to error pages. But before every HttpGet and Post action in my controllers I am checking if session is active.
So in case of a situation where session values are lost and not able to read them.
How can I implement this thing in any other way?
You need to check whether the session exists, has the fields you expect and is active. If the session does not exist or does not have a fields you expect, then handle the case when the session does not exist yet/expired. If it is not active, then handle the case when the session is no longer active. If everything is ok, then handle the request normally. If the session expired, then handle it as expired.
to check about session, you can use an ActionFilter like this:
public class SessionActiveFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var activeSession = Session["user"];
if (activeSession == null)
//Here, do a redirect
base.OnActionExecuting(filterContext);
}
}
Also, you can use a third option to save the session, like Redis Cache http://blogs.msdn.com/b/webdev/archive/2014/05/12/announcing-asp-net-session-state-provider-for-redis-preview-release.aspx
I know this is a dead story now. But I post this answer for the new comers. Please see the nice tutorial in codeproject about how to check session values in Action Filters.
In a dynamic web application, the session is crucial to hold the information of current logged in user identity/data. So someone without authentication cannot have access to some Page or any ActionResult, to implement this kind of functionality, we need to check session exists (is not null) in every action which required authentication.So, the general method is as follows:
[HttpGet]
public ActionResult Home()
{
if(Session["ID"] == null)
return RedirectToAction("Login","Home");
}
We have to check the above 2 statements each time and in each ActionResult, but it may cause 2 problems.
Repeat Things: As per the good programming stranded, we don't have to repeat the things. Create a module of common code and access it multiple times/repeatedly
Code missing: We have to write code multiple times so it might happen some time we forget to write code in some method or we missed it.
How To Avoid?
The ASP.NET MVC provides a very great mechanism i.e., Action Filters. An action filter is an attribute. You can apply most action filters to either an individual controller action or an entire controller.
If you want to know more about action filter, please click here.
So we will create a custom Action Filter that handles session expiration and if session is null, redirect to Login Action.
Create a new class in your project and copy the following code:
namespace YourNameSpace
{
public class SessionTimeoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if (HttpContext.Current.Session["ID"] == null)
{
filterContext.Result = new RedirectResult("~/Home/Login");
return;
}
base.OnActionExecuting(filterContext);
}
}
}
Now our Action Filter is created and we are ready to use it. The following code will show you how we can apply attribute to Action or to complete controller.
Apply to Action
[HttpGet]
[SessionTimeout]
public ActionResult MyProfile()
{
return View();
}
Apply to Controller
[SessionTimeout]
public class HomeController : Controller
{
}
Now all actions of Home Controller will check for session when hit with the help of Action Filter. So we have reduced the code and repetitive things. This is the benefits of Action Filters.
I am trying to look for a more clean way to add audit trail function to an exist asp.net MVC and Web Api project which contains hundreds of Controller and ApiController.
The Audit trail log would look like below. Basically I just want to log In what time who did what in this function.
UserID
ActionTime
Controller
Action
Anything I missed ? If there is . Please correct me. Thanks.
Currently I found there are some ways to make it .
Implement an ActionFilterAttribute and write my own log function in the OnActionExecuting, and then decorate all the actions with this attribute.
Implement a base Controller like BaseController for all the exist controller. And write log in the OnActionExecuting. Then change all the controller to inherit from BaseController. (If it is wrong . Please correct me . Thanks.)
For the ApiController. Implement a DelegatingHandler to make it.
For 1 and 2. I need change to all the exist code to make it. like change base class or decorate with new attribute. Considering in my case, This will be a hard work. Because thousands of class or methods need to be changed . I thinks it is kind of verbose. So I wondered if there is some clean way like 3 for ApiController to make it. Thanks.
I find that using global action filters is the best way to handle cross-cutting/aspect-oriented concerns such as this.
Note that this code is not tested.
public class AuditFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var userName = HttpContext.Current.User.Identity.Name;
var time = DateTime.Now.ToString(CultureInfo.InvariantCulture);
var controllerName = actionContext.ControllerContext.ControllerDescriptor.ControllerName;
var actionName = actionContext.ActionDescriptor.ActionName
Logger.Log(string.Format("user: {0}, date: {1}, controller {2}, action {3}", userName, time, controllerName, actionName));
}
}
And somewhere in your application startup pipeline, register the filter globally:
GlobalConfiguration.Configuration.Filters.Add(new AuditFilter());
Are you using a DI container? If you are or want to use a DI container, that could intercept all requests to the controllers. That way you don't change codes in hundreds of controllers, albeit simple change.
Here's Castle Windsor DI.
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)_kernel.Resolve(controllerType);
}
}
Have a look at the examples on this site if you intended to use it. I am sure there is a way to use it for both Web API and MVC controllers.
I am using MVC 4 Web API to create a service layer for an application. I am trying to create a global filter that will act on all incoming requests to the API. Now I understand that this has to be configured differently than standard MVC global action filters. But I'm having issues getting any of the examples I'm finding online to work.
The problem I am running into is in registering the filter with Web API.
I have my Global.asax set up like this...
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
MVCConfig.RegisterRoutes(RouteTable.Routes);
MVCConfig.RegisterGlobalFilters(GlobalFilters.Filters);
WebApiConfig.RegisterRoutes(GlobalConfiguration.Configuration);
WebApiConfig.RegisterGlobalFilters(GlobalConfiguration.Configuration.Filters);
}
}
My standard Mvc routing and filters work correctly. As does my WebApi routing. Here is what I have for my webApi filter registration...
public static void RegisterGlobalFilters(System.Web.Http.Filters.HttpFilterCollection filters)
{
filters.Add(new PerformanceTestFilter());
}
And here is the PerformanceTestFilter...
public class PerformanceTestFilter : ActionFilterAttribute
{
private readonly Stopwatch _stopWatch = new Stopwatch();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_stopWatch.Reset();
_stopWatch.Start();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
_stopWatch.Stop();
var executionTime = _stopWatch.ElapsedMilliseconds;
// Do something with the executionTime
}
}
This filter works fine when it is registered with the standard Mvc GlobalFilterCollection, but when I try to register it with System.Web.Http.Filters.HttpFilterCollection I get an error saying that it is not assignable to parameter type System.Web.Http.Filters.IFilter.
So I'm assuming that my PerformanceTestFilter needs to inherit from something other than ActionFilterAttribute in order to be registered as a webapi filter. I'm just not sure what that needs to be.
I imagine I will need to create two individual filters to work with mvc and webapi respectively. If there is a way to create a filter that could be registered to both, that would be great. But my primary concern is simply to get it working for webapi.
Thanks
The following should work. We actually use this for our web API project.
GlobalConfiguration.Configuration.Filters is of type HttpFilterCollection
var filters = System.Web.Http.GlobalConfiguration.Configuration.Filters;
filters.Clear();
filters.Add(new ValidationActionFilterAttribute());
public class ValidationActionFilterAttribute : FilterAttribute, IActionFilter, IFilter
{
...
}
Also, if you're working in a project that contains both MVC and WebAPI assembilies, could you check what's the namespace your
ActionFilterAttribute's namespace. It's fairly confusing cause there
are two ActionFilterAttributes under both:
System.Web.Http.Filters
System.Web.Http.Mvc
Source: Why is my ASP.NET Web API ActionFilterAttribute OnActionExecuting not firing?
It appears that you will need to have two filters, one for API and one for MVC. You can factor the common code into a separate class, and then just use the specific filter to call through to your common class, thus not violating DRY and essentially using the actual filters as wrappers which can be registered as filters.
We are creating MVC3 applications. We are using default editors and model state validation. We need to log application errors, but we prefer to make it by some kind of a global handler. We have a handler for unhandled exceptions, but we also want to log model state errors.
The question is: Where can we attach our logger to log such errors? Can we somehow override ModelState or detect situation when model served to view has model errors?
Global filters will most likely be your best way to go.
More from SO here: asp.net mvc 3 handleerror global filter always shows IIS status 500 page
Or checkout the msdn doc here:
http://msdn.microsoft.com/en-us/library/gg416513(v=vs.98).aspx
Create a attribute to handle error and register it in the controller,
public class ErrorHandlerAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext exceptionContext)
{
LogManager.GetLogger("somelogger").Error(exceptionContext.Exception.Message,exceptionContext.Exception);
base.OnException(exceptionContext);
}
}
register it in the controller like this,
[EwmsErrorHandler(ExceptionType = typeof(base type of exception to handle), View = view to redirect)]
public class MyController : Controller
{
<controller code>
}