Search code examples
asp.net-mvchttp-postchild-actions

Asp.Net mvc nested actions HTTPPOST


i ve got a weird problem. My view :

@{
   ViewBag.Title = "Index";
}

<h2>Index</h2>
@using(Html.BeginForm())
{
     <input type="submit"  value="asds"/>
}
@Html.Action("Index2")

My Controller:

public class DefaultController : Controller
{
    //
    // GET: /Default1/

    [HttpPost]
    public ActionResult Index(string t)
    {
        return View();
    }


    public ActionResult Index()
    {
        return View();
    }

    //
    // GET: /Default1/

    [HttpPost]

    public ActionResult Index2(string t)
    {
        return PartialView("Index");
    }

            [ChildActionOnly()]
    public ActionResult Index2()
    {
        return PartialView();
    }
}

When i click on a button [HttpPost]Index(string t) is executed, wich is fine. But after that [HttpPost]Index2(string t) is excuted and thats really weird to me because i ve posted data for Index action not for Index2. My logic tells me that [ChildActionOnly()]ActionResult Index2() instead of HttpPost one.

Why is this happening? How can override this behaviour without renaming [HttpPost]Index2 action?


Solution

  • That's the default behavior. It is by design. If you cannot change the POST Index2 action name you could write a custom action name selector that will force the usage of the GET Index2 action even if the current request is a POST request:

    public class PreferGetChildActionForPostAttribute : ActionNameSelectorAttribute
    {
        public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
        {
            if (string.Equals("post", controllerContext.HttpContext.Request.RequestType, StringComparison.OrdinalIgnoreCase))
            {
                if (methodInfo.CustomAttributes.Where(x => x.AttributeType == typeof(HttpPostAttribute)).Any())
                {
                    return false;
                }
            }
            return controllerContext.IsChildAction;
        }
    }
    

    and then decorate your two actions with it:

    [HttpPost]
    [PreferGetChildActionForPost]
    public ActionResult Index2(string t)
    {
        return PartialView("Index");
    }
    
    [ChildActionOnly]
    [PreferGetChildActionForPost]
    public ActionResult Index2()
    {
        return PartialView();
    }