ActionFilterAttribute on Controller vs action method - c#

I have a LoggingAttribute which logs request and response in OnActionExecuted method:
public class LoggingAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext httpContext)
{
//Logger.Log();
}
}
There is another attribute for validating the request and return BadRequest. This return response from OnActionExecuting method:
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, modelState);
}
}
}
Now, when I apply both these attributes on Action method, my BadRequests are not being logged but when I apply LoggingAttribute on controller level and ValidateModelAttribute on action method, BadRequests are getting logged (OnActionExecuted of LoggingAttribute getting called).
Can someone please explain this behaviour i.e. OnActionExecuted getting called even when action method is not being executed when attribute applied at controller.

You need to apply LoggingAttribute first on action method,
[LoggingAttribute]
[ValidateModelAttribute]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}

I tried you scenario like as below at my end
[HttpGet]
[ValidateModelAttribute]
[LoggingAttribute]
public void test()
{
//throw new NotImplementedException("This method is not implemented");
}
With the same code as yours and I found same issue as you , your LogginAttribute not get called because you are setting repose for context in your ValidateModelAttribute, as request get response it returns immediately(because of this actionContext.Response = ) as request got response , and then it even doesnt call you method on which you applied attribute.
So solution for this part is you have to write OnActionExecuting which get called before your Validationattribute OnActionExecuting method, and your code will log as OnActionExecuting method of LoggingAttribute before you are returning response.
public class LoggingAttribute : ActionFilterAttribute
{
public override void OnActionExecuting
(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Logger.Log();
}
public override void OnActionExecuted(HttpActionExecutedContext httpContext)
{
//Logger.Log();
}
}
and also change order or attribute , reason for doing below is when Response is set then in that case it get return from that point only , so whatever filter is there in pipe line after than is not get called.
[HttpGet]
[LoggingAttribute]
[ValidateModelAttribute]
public void test()
{
//throw new NotImplementedException("This method is not implemented");
}
as suggested in below answer by #programtreasures

Related

Headers added by ActionFilterAttribute will not appear in ApiController

I want to be able to inject headers to WebApi controller method context using an ActionFilterAttribute:
public class HeaderInjectionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
actionContext.Request.Headers.Add("test", "test");
base.OnActionExecuting(actionContext);
}
}
and using this in a controller
[HeaderInjectionFilter]
public class MotionTypeController : ApiController
{
public bool Get()
{
// will return false
return HttpContext.Current.Request.Headers.AllKeys.Contains("test");
}
}
As I stated out in the comment the header injected by the Filter will not be part of the HttpContext.Current. When I set a breakpoint on the last line of OnActionExecuting in the attribute I can see that it is containing the header value in the request headers.
If I change my controller to
public class MotionTypeController : ApiController
{
public bool Get()
{
HttpContext.Current.Request.Headers.Add("test", "test");
// will return true
return HttpContext.Current.Request.Headers.AllKeys.Contains("test");
}
}
everything will work so I guess the actionContext of the OnActionExecuting is not the same as the HttpContext.Current of the controller.
How can I inject headers for debugging purposes?
As I stated out in the comment the header injected by the Filter will
not be part of the HttpContext.Current
That's because you added it to the actionContext.Request.Headers collection. So make sure you are looking in the same place where you added it:
[HeaderInjectionFilter]
public class MotionTypeController : ApiController
{
public bool Get()
{
return this.Request.Headers.GetValues("test").Any();
}
}
and just forget about HttpContext.Current. Think of it as something that doesn't exist. Everytime someone uses HttpContext.Current in an ASP.NET application a little kitten dies.

ASP MVC ActionFilterAttribute OnActionExecuting not fired

I have 2 controllers Home with
public class HomeController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// do some irrelevant stuff
base.OnActionExecuting(filterContext);
}
public ActionResult Index()
{
return View();
}
}
and Service with
public ActionResult Confirm()
{ return RedirectToAction("Index", "Home");}
And one ActionFilterAttribute with OnActionExecuting method
public class InvitationModeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// do some stuff
base.OnActionExecuting(filterContext);
}
}
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new InvitationModeAttribute());
}
}
When I go to localhost/Service/Confirm , OnActionExecuting is fired, but then when RedirectToAction is called, OnActionExecuting is not fired.
How can I catch this after RedirectToAction?
Thanks
Refer this For More clarity
First of all
Remove OnActionExecuting method in controller level
public class HomeController : Controller
{
[InvitationModeAttribute]
public ActionResult Index()
{
return View();
}
}
2nd Controller
public class ServiceController : Controller
{
[InvitationModeAttribute]
public ActionResult Confirm()
{
return RedirectToAction("Index", "Home");
}
}
From MSDN
Scope of Action Filters
In addition to marking individual action methods with an action
filter, you can mark a controller class as a whole with an action
filter. In that case, the filter applies to all action methods of that
controller. Additionally, if your controller derives from another
controller, the base controller might have its own action-filter
attributes. Likewise, if your controller overrides an action method
from a base controller, the method might have its own action-filter
attributes and those it inherits from the overridden action method. To
make it easier to understand how action filters work together, action
methods are grouped into scopes. A scope defines where the attribute
applies, such as whether it marks a class or a method, and whether it
marks a base class or a derived class.

How do I disable an MVC controller by code?

I am using SelfHost/Katana/Owin for my WebServer.
I have a Controller in there that I want to enable/disable by code depending on a command line argument at launch time.
Is there a simple way of doing this in MVC?
Right now I'm thinking in the Controller's code to return HTTP-NotFound status code when this config is disabled, any better ideas?
You could decorate your controller with a custom Action Filter.
public class ConfigActionFilter : ActionFilterAttribute {
// This method is called before a controller action is executed.
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if(someConfigSetting) {
filterContext.Result = new RedirectToRouteResult("Error", someRouteValues);
}
}
...
}
Usage:
[ConfigActionFilter]
public class MyController : Controller {
...
}
More here.
You could perform a redirecttoaction that will take users to a different controller explaining what's happening.
ie:
public class MyController : Controller {
private IConfigReader _configReader;
public MyController(IConfigReader configReader){ //not sure if you're doing dependency injection or not, so I'm injecting it
_configReader = configReader;
}
public ActionResult Index() {
if(!_configReader.IsEnabled) {
return RedirectToAction("Denied", "AuthController");
}
//etc
return View();
}
}
You could create an attribute, apply it to the controller and set a static property on that attribute at startup time, and deny access (or return "Not found") when the flag is set.
Alternatively, you can implement a custom AuthorizationAttribute and put it on your controller
public class AuthorizationAdminAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (/*check for argument*/)
{
return false;
}
return true;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (AuthorizeCore(filterContext.HttpContext))
{
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else
{
filterContext.Result = new HttpNotFoundResult();
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}

Web Api ignores CreateErrorResponse in Attribute

I have a base controller:
public abstract class EntityController<T> : ApiController
{
[HttpPost]
[ValidateModel]
public abstract IHttpActionResult Create(T dto);
[HttpPut]
[ValidateModel]
public abstract IHttpActionResult Update(T dto);
[HttpDelete]
public abstract IHttpActionResult Delete(int id);
}
And a derived controller:
public class CustomerTypeController : EntityController<CustomerTypeDTO>
{
[ApiAuthorize(Right = Rights.CustomerType, Action = Action.Create)]
public override IHttpActionResult Create(CustomerTypeDTO customerType)
{
return Save(customerType);
}
[ApiAuthorize(Right = Rights.CustomerType, Action = Action.Update)]
public override IHttpActionResult Update(CustomerTypeDTO customerType)
{
return Save(customerType);
}
private IHttpActionResult Save(CustomerTypeDTO customerType)
{
//service layer save customer type
}
}
The base controller has a [ValidateModel] on the actions, whilst the derived controller sets it's own [ApiAuthorize] attribute. The attributes are fired in the right order (first ApiAuthorize and then ValidateModel). I have not set the order and I don't know if it's possible.
The ApiAuthorize looks like this:
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
public class ApiAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
//Do some checks...
if (!authorized)
actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, new CustomNotAuthorizedException());
}
}
However the problem is that even although the attribute creates an error response, the action still proceeds further by going into ValidateModel and then into the action itself.
How is it possible to stop the api from processing the request further at the Authorize and ValidateModel levels?
The problem was that the Authorize attribute code was not setting the response. It has to be like this:
if (!authorized)
{
var response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, new CustomNotAuthorizedException());
actionContent.Response = response;
}

asp net web api custom filter and http verb

I'm using a custom filter to validate the content type, like:
public override void OnActionExecuting(HttpActionContext httpActionContext)
{
List<String> errors = new List<String>();
// a
if (httpActionContext.Request.Content.Headers.ContentType.MediaType == "application/json")
{
}
else
{
errors.Add("Invalid content type.");
}
// more checks
}
The above code is working fine, but the validation should check the request http verb, because it should validate the content type only for put or post. I don't want to remove the custom filter from httpget actions because I have more checks inside it, and I don't want to split the filter in two parts, meaning I have to check the http verb inside the filter, but I can't find how.
Any tips?
You can get the method type (post or put) from this:
public override void OnActionExecuting(HttpActionContext actionContext)
{
string methodType = actionContext.Request.Method.Method;
if (methodType.ToUpper().Equals("POST")
|| methodType.ToUpper().Equals("PUT"))
{
// Your errors
}
}
If you need to get the HTTP Method of the request being validated by the filter, you can inspect the Method property of the request:
var method = actionContext.Request.Method;
I would recommend however that you break the filter apart, as you are quickly headed towards a big ball of mud scenario.
You really should be using the standard HTTPVerb attributes above your controller methods:
[HttpGet]
[HttpPut]
[HttpPost]
[HttpDelete]
[HttpPatch]
MVC Controllers for multiple:
[AcceptVerbs(HttpVerbs.Get, HttpVerbs.Post)]
WebAPI Controlelrs for multiple
[AcceptVerbsAttribute("GET", "POST")]
In the constructor of the action filter, you can pass in options/named parameters that will set the settings for the OnActionExecuting logic. Based on those settings you can switch up your logic.
public class MyActionFilterAttribute : ActionFilterAttribute
{
private HttpVerbs mOnVerbs;
public MyActionFilterAttribute(HttpVerbs onVerbs)
{
mOnVerbs = onVerbs;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentVerb = filterContext.HttpContext.Request.HttpMethod;
if (mOnVerbs.HasFlag(HttpVerbs.Post)) { }
else if (mOnVerbs.HasFlag(HttpVerbs.Get)) { }
base.OnActionExecuting(filterContext);
}
}
[MyActionFilter(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Index()
{
}

Categories