Amir Jalali
Amir Jalali

Reputation: 3212

Asp.net mvc How to prevent browser from calling an action method?

I have two actions inside my controller (shoppingCartController)

    public ActionResult Index()
    {
        //some stuff here
        return View(viewModel);
    }


    public ActionResult AddToCart(int id)
    {

        return RedirectToAction("Index");

    }

Is there anyway to prevent the users from directly calling the index action by typing the url in the browser?

For example: If the user browses to shoppingCart/index be redirected to Home/Index.

Upvotes: 15

Views: 45058

Answers (8)

Linda
Linda

Reputation: 21

NonAction is the attribute to use. It is part of the MVC library, so you don't have to create your own attribute.

Represents an attribute that is used to indicate that a controller method is not an action method.

Upvotes: 2

B M
B M

Reputation: 1882

You also can use child action only attribute. Just sharing :)

public class MyController {

  [ChildActionOnly]
  public ActionResult Menu() {
    var menu = GetMenuFromDB();
      return PartialView(menu);
  }

}

Upvotes: 0

Anjan Kant
Anjan Kant

Reputation: 4316

Here is written code how to prevent browser direct access to action method: Write below code in FilterConfig.cs

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class NoDirectAccessAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.UrlReferrer == null ||
                    filterContext.HttpContext.Request.Url.Host != filterContext.HttpContext.Request.UrlReferrer.Host)
            {
            filterContext.Result = new RedirectToRouteResult(new
                           RouteValueDictionary(new { controller = "Home", action = "Index", area = "" })); 
        }
    }
}

Now apply this code on your action method

[NoDirectAccess]
public ActionResult MyActionMethod()

This will restrict to call directly any class or action method.

Upvotes: 1

bmks
bmks

Reputation: 39

If SessionState is enabled, you can use controller's TempData to achieve your goal. Set TempData in AddToCart action, and only display Index view if Index action can get TempData key set in AddToCart action.

If you need this for multiple actions/projects, use combination of custom Action Filter and ActionResult. Like this:

// Controller
[PreventDirectAccess]
public ActionResult Index()
{
    //some stuff here
    return View(viewModel);
}

public ActionResult AddToCart(int id)
{
    return new PreventDirectAccessRedirectToRouteResult(new RouteValueDictionary
    {
        {"action", "Index"}
    });
}

// Filter
public class PreventDirectAccessAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext == null)
            throw new ArgumentNullException("filterContext");

        if (filterContext.Controller.TempData[PreventDirectAccessRedirectToRouteResult.Executed] == null)
            filterContext.Result = new HttpNotFoundResult();

        base.OnActionExecuting(filterContext);
    }
}

// ActionResult
public class PreventDirectAccessRedirectToRouteResult : RedirectToRouteResult
{
    public const string Executed = "PreventDirectAccessRedirectExecuted";

    public override void ExecuteResult(ControllerContext context)
    {
        context.Controller.TempData[Executed] = true;
        base.ExecuteResult(context);
    }
}

Upvotes: 3

Dan
Dan

Reputation: 13343

This is untested, but I believe you can use the Validate Anti Forgery Token

In your code on the page, you will need to place an validation token in the form that is to be posted:

@Html.AntiForgeryToken()

Which produces:

<input name="__RequestVerificationToken" type="hidden" value="s9+jDREFMlNPkAT2zOlmhJZQbbDOzMhuarSTG1BVAC4GeHiNL5VtuQo7CQTF8obw8hEYIQac9YaQh+qVcF0xj0eNO7lVdezz+JxuSKGQo2d2gEdtkEdR+XTTFas4Gh6fjSYc7A1rWF8AAhxjZ9j6GlbRhECZOPAlPAItnjz49QQ=" />

This token is automatically picked up by any action that has this attribute:

[ValidateAuthenticationToken]
public ActionResult AddToCart(int id)
{
   return Index();
}

If the request is direct then an error will occur.

Upvotes: 0

Aleks
Aleks

Reputation: 1689

If all you are worried about is the user typing in the URL, then using the HttpPost attribute should prevent your action from being called that way:-

[HttpPost]
public ActionResult AddToCart(int id)
{

This prevents GET requests from calling that action. It doesn't however stop someone writing a dummy form and POSTing to your action.

If you are worried about something a little more malicious, you might want to implement some form of anti-forgery token, there's some good info on that here.

EDIT

OK, so on re-reading the question the above doesn't quite address your issue.

How about a route? if you had something like the below, it would prevent ShoppingCart/Index being called and redirect the user to your site index.

        routes.MapRoute(
            "ShoppingCartIndex",
            "ShoppingCart/Index",
            new { controller = "Home", action = "Index" }
        );

Upvotes: 2

Eric King
Eric King

Reputation: 11734

You could use the [ChildActionOnly] attribute on your action method to make sure it's not called directly, or use the ControllerContext.IsChildAction property inside your action to determine if you want to redirect.

For example:

public ActionResult Index()
{
    if(!ControllerContext.IsChildAction)
    {
       //perform redirect here
    }

    //some stuff here
    return View(viewModel);
}

If you can't make the Index action a child action, you could always check the referrer, understanding that it's not foolproof and can be spoofed. See:

How do I get the referrer URL in an ASP.NET MVC action?

Upvotes: 18

Maheep
Maheep

Reputation: 5605

Try making this Index controller action as private. A method with private access modifier should not be accessible from outside class.

And then, rahter than calling RedirectToAction from AddToCart call it as simple method like below:

private ActionResult Index()
{
    //some stuff here
    return View(viewModel);
}


public ActionResult AddToCart(int id)
{

    return Index();

}

Upvotes: 4

Related Questions