Make all action methods of a controller ChildActionOnly

672 Views Asked by At

Instead of having to remember to decorate a bunch of action methods with the ChildActionOnly attribute, it would be convenient to be able to specify all methods in an entire controller as such.

Trying to put the ChildActionOnly attribute on the controller class doesn't work (at least in my code context) because during dependency injection for the controllers, which occurs at an early phase in the request pipeline, there is no HttpContext or Request object, and the error "Request is not available in this context" is thrown.

Could I create a RouteConstraint that makes the route itself enforce ChildActionOnly? That seems doubtful because of the same request pipeline issue--I don't know if the HttpContext would be available during the time that execution of RouteConstraints occurs. If you have ideas how to implement this, please share.

Maybe create a unit test that uses reflection to discover all action methods of a specific controller and ensure they have the ChildActionOnly attribute set...

How do I accomplish this? Could you give some starter code (doesn't have to be polished or even working, just a starting point will help).

2

There are 2 best solutions below

2
On BEST ANSWER

how about filters? you can create your custom filter and then just add the filter to FilterConfig. Filters in MVC allow you to add you business logic(code) before/after an action is executed, in the example for every request to an action, it will execute first and then the action, and you can have access to:

HttpContext.Current.Session filterContext.HttpContext.Request

and you can redirect

filterContext.Result = new RedirectResult.

Exist different types of filter: 1. Authentication filters (New in ASP.NET MVC5) 2. Authorization filters 3. Action filters 4. Result filters 5. Exception filters

in the example below, i create a custom filter for Authorization

 public class CustomFilter : FilterAttribute, IAuthorizationFilter
 {
    public void OnAuthorization(AuthorizationContext filterContext)
    {

       if (!UserRepository.IsValidUserBy(HttpContext.Current.Session["userName"].ToString()))
            {
                filterContext.Result = new RedirectResult("~/User/AccessDenied", true);
            }
    }
   }

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new MyExpirePageActionFilterAttribute());
       filters.Add(new CustomFilter ());

    }
0
On

I found this question and answer whilst googling how to achieve exactly this myself.
Whilst this question specifically targets MVC4, and my answer doesn't apply directly to that, it still seemed relevant to point out that MVC 5 now supports using the [ChildActionOnly] attribute on an entire Controller, making the following possible:

namespace MyWebApp.Controllers
{
   [ChildActionOnly]
   public class MyController
   {
      //Can only be called from another action
      public ActionResult MyAction()
      {
         return View();
      }
   }
}

which, when navigating directly to http://myapp.com/MyController/MyAction results in the following being returned:

Server Error in '/' Application.

The action 'MyAction' is accessible only by a child request. 
  Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace

for more information about the error and where it originated in the code.

 Exception Details: System.InvalidOperationException: The action 'MyAction' is accessible only by a child request.