ASP.NET MVC: Enforce AJAX request on an action - asp.net-mvc

I'm looking for a way to enforce a controller's action to be accessed only via an AJAX request.
What is the best way to do this before the action method is called? I want to refactor the following from my action methods:
if(Request.IsAjaxRequest())
// Do something
else
// return an error of some sort
What I'm envisioning is an ActionMethodSelectorAttribute that can be used like the [AcceptVerbs] attribute. I have no experience crating such a custom attribute though.

Create an ActionFilter that fires OnActionExecuting
public class AjaxActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
filterContext.Result = new RedirectResult(//path to error message);
}
}
Setting the filter's Result property will prevent execution of the ActionMethod.
You can then apply it as an attribute to your ActionMethods.

Its as simple as this:
public class AjaxOnly : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
return controllerContext.HttpContext.IsAjaxRequest();
}
}
I just forget where IsAjaxRequest() comes from, I'm pasting from code I have but "lost" that method. ;)

Related

Using AttributeAuthorization For Global Filter

I have register a global filter to authorize requests that need a cookie but I have a controller that needs to be public so I add [AllowAnonymous] attribute to the controller methods but my filter still fires and keeps redirecting. I'm not sure the best way to fix this issue.
Do I need to modify my onauthorization method to look for the [AllowAnonymous] attribute?
public class CookieAuthFilter : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
HttpCookie cookie = filterContext.HttpContext.Request.Cookies.Get("token");
if (cookie == null)
{
filterContext.Result = new RedirectResult("/Home/Index");
}
}
}
Do I need to modify my onauthorization method to look for the [AllowAnonymous] attribute?
You could, but it would be simpler just to move your logic so the base OnAuthorize method (which contains the logic to scan for [AllowAnonymous]) is unmodified.
public class CookieAuthFilter : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return httpContext.Request.Cookies.Get("token") != null;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectResult("/Home/Index");
}
}
It is better to use AuthorizeCore to return true if the user is authorized, and use HandleUnauthorizedRequest for the redirect.
OnAuthorization also contains some additional logic to help it deal with output caching that you should leave in place.

Authorize a parameter for an action in MVC5

I commonly need to authorize a particular parameter to be evaluated in a service call within an action in MVC5. For instance, let's say that my action is public ActionResult Edit(string partnerName).
Today, I handle this by always evaluating if (!User.CanAccessPartnerModule(THIS_MODULE_ID, partnerName)) throw new UnauthorizedException();
However, I would like to be able to do something like this:
[Authorize(Roles = THIS_MODULE_ID)]
public ActionResult Edit([AuthorizePartnerModule(THIS_MODULE_ID)] string partnerName)
{
...
}
To be clear, 1) I don't think the AuthorizeAttribute would be necessary if this were implemented as I envision, and 2) the thing that doesn't exist is the AuthorizePartnerModuleAttribute.
Is there a ready-made attribute or tutorial that explains how this may be accomplished? And if not, is this not advisable to do?
You could extend authorization with a custom authorization filter by creating a subclass of AuthorizeAttribute
using System.Web;
using System.Web.Mvc;
namespace Filters
{
public class AuthorizePartnerModule : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// getting the parameter from the request
string partnerName = httpContext.Request.Params["groupId"].ToString();
// custom validation
return User.CanAccessPartnerModule(THIS_MODULE_ID, partnerName);
}
}
}
And then, you could validate your action method with:
[AuthorizePartnerModule(Roles = THIS_MODULE_ID)]
public ActionResult Edit(string partnerName)
{
...
}
Another option would be to create a custom ActionFilter (an implementation of IActionFilter). An ActionFilter implements two methods:
OnActionExecuting is executed right before the action method
OnActionExecuted is executed right after the action method execution.
So, you could make the necessary validation with something like:
using System.Web.Mvc;
namespace Filters {
public class AuthorizePartnerModule : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
// getting the parameter from the request
string partnerName = filterContext.ActionParameters["partnerName"].ToString();
// custom validation
if (!User.CanAccessPartnerModule(THIS_MODULE_ID, partnerName))
{
filterContext.Result = new HttpNotFoundResult();
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// do nothing
}
}
}
In this case, however ,you would have to validate like that:
[Authorize(Roles = THIS_MODULE_ID)]
[AuthorizePartnerModule]
public ActionResult Edit(string partnerName)
{
...
}

Extending ASP.NET MVC Controller with redirection or 404 error in certain circumstances

I want to extend the Controller class to check whether a request matches certain criteria.
In particular, I want to check whether the user has been directed from a certain URL using the UrlReferrer proprety.
If the user has not come from a particular URL, it should return a 404 message, or in some cases redirect to a different URL altogether.
I was overriding OnActionExecuting to look like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
return new HttpNotFoundResult();
//or in some cases I want to redirect to a completely different URL.
}
}
But this does not work because its a void method.
How would I go about performing this task?
Thanks,
Mike
Even though the other answer is correct, I don't think it's a good solution for your scenario. I think what you need is a custom Action Filter like this:
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
filterContext.Result = new HttpNotFoundResult();
}
}
}
Then, you can use this filter either at the Controller level (to apply it to all Actions in the Controller) or at the Action level:
[MyActionFilter]
public class HomeController : Controller
{
}
Yes, it's a void method, but inside the filterContext parameter you have a Result property. If the result is filled by the time the OnActionExecuting method finishes, the actual Action method is not even going to be called.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
filterContext.Result = new HttpNotFoundResult();
}
}
Alternatively, if you want this behavior to be consistent across different controllers, you might want to consider writing a filter instead. To understand more about it, please refer to: http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
I've just finished implementing a feature such as this within our primary intranet application. My solution was to create a custom authorize filter as suggested by ataravati above,but to redirect according to certain circumstances.
public class UrCustomFilter:ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsAuthenticRequest())
{
if( cond1 == true)
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "Error" }, {"action", "404NotFound" } });
else{
//do another redirect here
}
}
}
}

Changing the view in an ASP.NET MVC Filter

I want to redirect the user to a different view if they are using a mobile browser. I've decided I'd like to do this using MVC filters by applying it to actions which I want to have a mobile view.
I believe this redirect needs to happen in OnActionExecuted, however the filterContext does not contain information on the view - it does, however in OnResultExecuted, but by this time I believe it is too late to change the view.
How can I intercept the view name and change the ViewResult?
This is what I have in the result executed and what I'd like to have work in Action Executed.
public class MobilePageFilter : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if(filterContext.Result is ViewResult)
{
if (isMobileSite(filterContext.HttpContext.Session[SetMobile.SESSION_USE_MOBILE]))
{
ViewResult viewResult = (ViewResult)filterContext.Result;
string viewName = viewResult.ViewName;
filterContext.Result = new ViewResult
{
ViewName = "Mobile/" + viewName,
ViewData = viewResult.ViewData,
TempData = viewResult.TempData
};
}
}
base.OnResultExecuted(filterContext);
}
}
I would recommend you the following blog post which explains a better alternative to achieve what you are asking for rather than using action filters.
This is what I ended up doing, and wrapped up into a reusable attribute and the great thing is it retains the original URL while redirecting (or applying whatever result you wish) based on your requirements:
public class AuthoriseSiteAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// Perform your condition, or straight result assignment here.
// For me I had to test the existance of a cookie.
if (yourConditionHere)
filterContext.Result = new SiteAccessDeniedResult();
}
}
public class SiteAccessDeniedResult : ViewResult
{
public SiteAccessDeniedResult()
{
ViewName = "~/Views/SiteAccess/Login.cshtml";
}
}
Then just add the attribute [SiteAccessAuthorise] to your controllers you wish to apply the authorisation access to (in my case) or add it to a BaseController. Make sure though the action you are redirecting to's underlying controller does not have the attribute though, or you'll be caught in an endless loop!

MVC - Redirect inside the Constructor

I would like to know how am I able to redirect the request inside the controller constructor if I need to do it?.
For example:
Inside the constructor I need to initialize an object with an dynamic value, in some cases I don't want to do it and in that case I want to redirect to some other place.
At the same way the rest of the constructor will not be executed neither the "original following action".
How can I do it?
Thank you
EDIT #1
Initially I used:
public override void OnActionExecuting(ActionExecutingContext filterContext)
There I could redirect to some other controller/action/url, but later in time, I needed to change my controller, where I initialize an variable in his constructor and have some code that really needs to redirect the request :P
I need this also because the OnActionExecuting executes AFTER the controller constructor.
And in my logic, the redirect needs to be done there.
Performing redirects inside the controller constructor is not a good practice because the context might not be initialized. The standard practice is to write a custom action attribute and override the OnActionExecuting method and perform the redirect inside. Example:
public class RedirectingActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (someConditionIsMet)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "someOther",
action = "someAction"
}));
}
}
}
and then decorate the controller which you would like to redirect with this attribute. Be extremely careful not to decorate the controller you are redirecting to with this attribute or you are going to run into an endless loop.
So you could:
[RedirectingAction]
public class HomeController : Controller
{
public ActionResult Index()
{
// This action is never going to execute if the
// redirecting condition is met
return View();
}
}
public class SomeOtherController : Controller
{
public ActionResult SomeAction()
{
return View();
}
}

Resources