Calling a Method After a Controller Action in ASP.NET MVC - asp.net

I used to believe that the following method would be invoked after all controller methods are done at the end:
protected override void EndExecute(IAsyncResult asyncResult)
Now all override methods are called and then, controller methods are invoked. Do you know what override method is invoked after all methods?

You can use action filter it will be called every time any method execute. You can filter it by matching method name.
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = String.Format("{0} controller:{1} action:{2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
For more details please visit : https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/controllers-and-routing/understanding-action-filters-cs

Related

How can I add a header in OnResultExecuted

I need to add a header to the response.
The header value is based on the response body.
When I try to add the header I get an error: 'Headers are read-only, response has already started.'
public class SecurityFilter : ActionFilterAttribute
{
public override async void OnActionExecuting(ActionExecutingContext context)
{
var body = await new StreamReader(context.HttpContext.Request.Body).ReadToEndAsync();
}
public override void OnResultExecuted(ResultExecutedContext context)
{
var objectResult = context.Result as ObjectResult;
var resultValue = objectResult.Value;
Console.WriteLine(resultValue);
context.HttpContext.Response.Headers.Add("foo", "bar");
base.OnResultExecuted(context);
}
}
OnResultExecuted method is called after the action result executes. Response headers can't be set/modified if the result has been done.
You can use OnActionExecuted method which is called after the action executes, before the action result. Or use OnResultExecuting method which is called before the action result executes.
Here is a simple demo you could follow:
public class SecurityFilter : ActionFilterAttribute
{
public override async void OnActionExecuting(ActionExecutingContext context)
{
}
public override void OnActionExecuted(ActionExecutedContext context)
{
var objectResult = context.Result as ObjectResult;
var resultValue = objectResult.Value;
Console.WriteLine(resultValue);
context.HttpContext.Response.Headers.Add("foo", "bar");
base.OnActionExecuted(context);
}
}

Asp.Net Register Global Custom Filter Not Having [ChildAactionOnly] attribute

I know how to Register Global filter that applies all actions with:
protected void Application_Start(){
GlobalFilters.Filters.Add(new MySweetCustomFiAttribute());
}
But instead, I need to register MySweetCustomFilterAttribute to actions not having (or having) [ChildActionOnly] attribute.
// Register MySweetCustomFilterAttribute
public ActionResult Index()
{
return View();
}
// Do not register MySweetCustomFilterAttribute
[ChildActionOnly]
public ActionResult MySweetChildAction()
{
...
}
You should put a bypass into your [MySweetCustomFiAttribute] to return if the action is decorated with [ChildActionOnly]. Like this:
if(filterContext.ActionDescriptor.IsDefined(typeof(ChildActionOnlyAttribute), false))
return;
Here the complete work-around with the help of #pedro-benevides
namespace tv2web.Filters{
public class MainModelFixAttribute : ActionFilterAttribute {
protected bool IsChild(ControllerContext filterContext) {
return filterContext.IsChildAction;
}
public override void OnResultExecuting(ResultExecutingContext filterContext) {
if (IsChild(filterContext)) return;
filterContext.Controller.ViewBag.mainModel = filterContext.Controller.ViewData.Model;
}
public override void OnResultExecuted(ResultExecutedContext filterContext) {
if (IsChild(filterContext)) return;
}
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (IsChild(filterContext)) return;
}
public override void OnActionExecuted(ActionExecutedContext filterContext) {
if (IsChild(filterContext)) return;
}
}
}

How to use Exception filters in MVC 5

How i can implement Exception Filters in MVC5.
I want to throw the exception to NLog and redirect the page to a default error page which displays "Something is gone wrong"
I have a Filter class as follows
using System;
using System.Diagnostics;
using System.Security.Policy;
using System.Web.Mvc;
using System.Web.Mvc.Filters;
using System.Web.Routing;
using System.Web.UI.WebControls;
using Delivros.UI.Controllers;
using Delivros.UI.Areas.User.ViewModel;
using System.Web;
namespace Delivros.UI.Filters
{
public class CustomAuthenticationFilter : IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
Debug.WriteLine("OnAuthenticationChallenge : MyAuthenticationFilter");
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MyAuthorizationFilter : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.Cookies[System.Configuration.ConfigurationManager.AppSettings[Convert.ToString(CookieField.cookieName)]] == null)
{
}
else
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "User" },
{ "action", "UserRegistration" } ,
{"Area","User"}
});
}
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomActionFilter : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
base.OnResultExecuted(filterContext);
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "User" },
{ "action", "UserRegistration" } ,
{"Area","User"}
});
// ActionResult home = new HomeController().Index();
}
}
public class MyResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
}
}
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "User" },
{ "action", "UserLogOut" } ,
{"Area","User"}
});
}
}
}
But nothing is redirecting to the page...
You could derive your own HandleErrorAttribute
public class NLogExceptionHandlerAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
// log error to NLog
base.OnException(filterContext);
}
}
Then register it globally
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new NLogExceptionHandlerAttribute());
...
}
By default, the HandleErrorAttribute will display the Error view located in the ~/Views/Shared folder but if you wanted to display a specific view you can set the View property of the attribute.
I believe it should be this code:
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new {
action = "UserLogOut",
controller = "User",
area = "User"
}));
}
}
You may add an additional "if (!filterContext.ExceptionHandled)" statement before logging the values inside the result to make sure that the exception's unhandled for the moment.
Exception filters are run only when unhandled exception has been thrown inside an action method. As you asked, here is an example to redirect to another page upon exception:
public class MyExceptionAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if(!filterContext.ExceptionHandled)
{
filterContext.Result = new RedirectResult("~/Content/ErrorPage.html");
filterContext.ExceptionHandled = true;
}
}
}
Now, to apply this filter to either controllers or individual actions, put [MyException] on them.
You may need to check the occurence of an specific Exception inside the if clause.
e.g.:
if(... && filterContext.Excaption is ArgumentOutOfRangeException)
To return a View as Exception Response:
filterContext.Result = new RedirectResult("/Home/ErrorAction");
other alternatives you might use to redirect are:
new RedirectToRouteResult{ ... }
new ViewResult{ ... }
Below worked for me. A few things to note 1) RedirectResult points to a controller action, not a view 2) you need to set filterContext.ExceptionHandled = true; or your exception view / page will not display.
public class ErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
Log.Logger.Error(filterContext.Exception, "An Unhandled exeption occured durring the execution of a request");
filterContext.Result = new RedirectResult("~/MyControler/MyErrorAction");
filterContext.ExceptionHandled = true;
}
}
Ofcourse you will also need to register the Error attribute using the GlobalFilters object from your Global.asax as shown below.
protected void Application_Start()
{
GlobalFilters.Filters.Add(new ErrorAttribute());
...
}

How do I get my EntitySetController to be visible to my route?

I created an EntitySetController that looks like this:
public class OrdersController : EntitySetController<Order,Guid>
{
private readonly PizzaCompanyEntities _context = Factories.DataFactory.GetPizzaContext();
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
public override IQueryable<Order> Get()
{
return _context.Orders;
}
protected override Order GetEntityByKey(Guid key)
{
var result = _context.Orders.FirstOrDefault(o => o.Id == key);
if (result == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return result;
}
}
In an existing MVC 4 web application.
I configure the route as follows:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapODataRoute("PizzaApi", "odata", GetImplicitEdm());
}
private static IEdmModel GetImplicitEdm()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Order>("Orders");
builder.EntitySet<Pizza>("Pizzas");
builder.EntitySet<Pizzas_To_Orders>("PizzasToOrders");
builder.EntitySet<Size>("Sizes");
builder.EntitySet<Status>("Statuses");
builder.EntitySet<Pizzas_To_Toppings>("PizzasToToppings");
return builder.GetEdmModel();
}
}
And execute the configuration as follows:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
But when I execute my route at http://localhost:29064/odata/Orders I am getting a 404 and a message "The controller for path /odata/Orders was not found or does not implement IController.
I cannot figure out what I am missing to get the route registered and the controller running. I have done a similar application from scratch and have not had this trouble.
How do I get my OData route working?

Get the route when unauthenticated/logout with mvc?

I have this piece of code:
public class Authenticate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.Redirect("/");
}
}
}
I was wondering if it is possible to make it redirect to the view for action="Login" controller="AdminLogin"? And how do I pass some message to the login view that tells "you need to login to access that" or similar?
/M
Here is how I solved the redirect-part:
public class Authenticate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
RedirectToRoute(filterContext,
new
{
controller = "AdminLogin",
action = "AdminLogin"
});
}
}
private void RedirectToRoute(ActionExecutingContext context, object routeValues)
{
var rc = new RequestContext(context.HttpContext, context.RouteData);
string url = RouteTable.Routes.GetVirtualPath(rc,
new RouteValueDictionary(routeValues)).VirtualPath;
context.HttpContext.Response.Redirect(url, true);
}
}
Not sure if it is optimal but seems to do the job correctly

Resources