umbraco mvc surface controller, can't return view from HttpPost Action

X-Dev picture X-Dev · May 30, 2013 · Viewed 16.2k times · Source

Overview of the problem:

I've created a Surface controller with an action that is called using @Html.Action(...).

The @Html.Action call is done within a Macro partial view and the macro is included within the content of a page using the rich text editor.

(I'm new to this so if i'm going about things the wrong way then please let me know.)

The Surface controller has a GET and a POST action but it's the get action called within the macro partial.

Get action renders fine, entering no data into the form will invalidate the model state (which is what i'm currently testing).

submitting the form (with no entered data) means i can step into my POST action, ModelState.IsValid is set to false and CurrentUmbracoPage() is returned.

All fine... No Exceptions encountered when debugging...

It's at this point that the error text "Error loading Partial View script" appears on the page.

All I'm trying to do is return the same page with the validation messages showing.

Details:

Umbraco v6.0.5

The Controller I'm currently working on is used to reset a user's password. I also have a login conroller that is getting around this issue by using RedirectToCurrentUmbracoPage().

to access the page that contains the macro i use the address http://{testhost}/Reset-Password the error text returned reads: Error loading Partial View script (file: ~/Views/MacroPartials/ResetPassword.cshtml)

code is within a seperate solution and views and bin directories are copied accross. nuget package UmbracoCMS.Scaffolding is used.

Controller code:

public class ResetPasswordSurfaceController : SurfaceController {        
        [ChildActionOnly]
        [HttpGet]
        public ActionResult Reset(string token, string email) {
             // Validation Code Omited             
             var user = Membership.GetUser(username);
             return PartialView("Reset", new ResetPasswordSurfaceModel { UserID =     user.ProviderUserKey.AsInt() });
        }

        [HttpPost]
        public ActionResult PostReset(ResetPasswordSurfaceModel model) {
            if (ModelState.IsValid) { 
                 //Password reset code omited                 
                  return RedirectToCurrentUmbracoPage();
             }
            //works but only partial view content is rendered
            // return PartialView("Reset",model);         
            return CurrentUmbracoPage();
        }
    }

View - ~\Views\ResetPasswordSurface\Reset.cshtml:

@model UmbracoExt.Models.ResetPasswordSurfaceModel
@using (Html.BeginUmbracoForm("PostReset", "ResetPasswordSurface")) {
      @Html.EditorForModel() 
    <input type="submit" value="Submit" />
}

Macro Partial View - ~\Views\MacroPartials\ResetPassword.cshtml:

@inherits Umbraco.Web.Macros.PartialViewMacroPage       
@Html.Action("Reset", "ResetPasswordSurface")

Any help is appreciated.

Edit:

Removing the [HttpGet] attribute from the Reset Action has revealed that after the PostReset action is called the Reset action is also called.

Renaming PostReset to Reset and re-adding the httpget attribute to the original Reset Action results in the post action being called twice. the second time it is called causes the exception: Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form

I have reverted the changes so i'm back at Reset ([HttpGet]) being called after the PostReset action.

So the problem still stands. How can i get around this issue? I need to return the result from the PostReset Action.

Answer

milos picture milos · Jul 7, 2013

This is how I solved this problem:

  1. I created extension method for model:

    public static class ExtensionMethods
    {
       public static void MapModel<T>(this WebViewPage<T> page) where T : class
       {
          var models = page.ViewContext.TempData.Where(item => item.Value is T);
    
          if (models.Any())
          {
             page.ViewData.Model = (T)models.First().Value;
             page.ViewContext.TempData.Remove(models.First().Key);
          }
       }
    }
    
  2. Controller code:

    [HttpPost]
    public ActionResult Index(MyModel model)
    {
        TempData.Add("MyModel", model);
        return RedirectToCurrentUmbracoPage();
    } 
    
  3. Partial view code:

     @using UmbracoTest.Extension
     @using UmbracoTest.Models
     @model MyModel
     @{
         this.MapModel<MyModel>();
      } 
    
     @using (Html.BeginUmbracoForm("Index", "Home", FormMethod.Post))
     { 
          <div>
            @Html.TextBox("Text", Model.Text )
          </div>
    
         <input type="submit" name="submit" value="Submit" />
     }