Sitecore MVC - 如何在页面上处理多个表单

时间:2021-12-02 11:22:00

I've been looking at Sitecore MVC but I'm stuck at how to handle a case where my page has two controller renderings and each contains a form. I want the individual controllers to handle their HttpPost and return the whole page after post.

我一直在寻找Sitecore MVC,但我仍然坚持如何处理我的页面有两个控制器渲染并且每个包含一个表单的情况。我希望各个控制器处理他们的HttpPost并在发布后返回整个页面。

I've set up a simple example. Both controllers are similar:

我已经建立了一个简单的例子。两个控制器都类似:

public class ExampleController : Sitecore.Mvc.Controllers.SitecoreController
{
    public override ActionResult Index()
    {
        return View("Index");
    }

    [HttpPost]
    public ActionResult Index(string formPostData)
    {
        ViewBag.SaveForLater = formPostData;
        return Index();
    }
}

The views look like this:

视图看起来像这样:

@using Sitecore.Mvc
@using (Html.BeginRouteForm(Sitecore.Mvc.Configuration.MvcSettings.SitecoreRouteName, FormMethod.Post))
{
    @Html.AntiForgeryToken()
    var term = ViewBag.SaveForLater as string;
    if (!string.IsNullOrEmpty(term))
    {
        <p>Submitted: @term</p>
    }
    <p>
        @Html.Sitecore().FormHandler("Example", "Index")
        <input type="text" name="formPostData" placeholder="Enter something" />
        <input type="submit" name="submit" value="Search" />
    </p>
}

With this setup both forms submit their data but the returned page consists only of the partial view and not the whole page.

使用此设置,两个表单都提交其数据,但返回的页面仅包含部分视图,而不是整个页面。

If I replace the line @Html.Sitecore().FormHandler("Example", "Index") with @Html.Sitecore().FormHandler() then the whole page is returned but the post action for both forms is processed.

如果我用@ Html.Sitecore()。FormHandler()替换@Html.Sitecore()。FormHandler(“Example”,“Index”)行,则返回整个页面,但处理两个表单的post动作。

Neither scenario is ideal. I must be missing something and would appreciate a pointer.

这两种情况都不理想。我必须遗漏一些东西,并会欣赏指针。

3 个解决方案

#1


13  

Unfortunately there are multiple ways how you can integrate with Sitecore MVC and Sitecore doesn't provide many best practice examples. One example you can find here.

不幸的是,有多种方法可以与Sitecore MVC集成,而Sitecore没有提供许多最佳实践示例。你可以在这里找到一个例子。

In our projects we do it a bit different, because we want to use as much as possible the conventions etc. from default ASP.NET MVC. I try to include a complete simple example in this post.

在我们的项目中,我们做的有点不同,因为我们希望尽可能多地使用默认ASP.NET MVC中的约定等。我试着在这篇文章中包含一个完整的简单例子。

We add two different hidden fields to the form with the current action and the current controller, the view looks like this:

我们使用当前操作和当前控制器向表单添加两个不同的隐藏字段,视图如下所示:

@model Website.Models.TestViewModel

@using (Html.BeginForm())
{
    @Html.LabelFor(model => model.Text)
    @Html.TextBoxFor(model => model.Text)

    <input type="submit" value="submit" />

    <input type="hidden" name="fhController" value="TestController" />
    <input type="hidden" name="fhAction" value="Index" />
}

With this simple ViewModel:

有了这个简单的ViewModel:

namespace Website.Models
{
    public class TestViewModel
    {
        public string Text { get; set; }
    }
}

Then we have created a custom attribute which checks if the current controller/action is the same as posted:

然后我们创建了一个自定义属性,用于检查当前控制器/操作是否与发布时相同:

public class ValidateFormHandler : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var controller = controllerContext.HttpContext.Request.Form["fhController"];
        var action = controllerContext.HttpContext.Request.Form["fhAction"];    

        return !string.IsNullOrWhiteSpace(controller)
            && !string.IsNullOrWhiteSpace(action)
            && controller == controllerContext.Controller.GetType().Name
            && methodInfo.Name == action;
    }
}

The controller actions then gets the new attribute:

然后控制器操作获取新属性:

namespace Website.Controllers
{
    public class TestController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        [ValidateFormHandler]
        public ActionResult Index(TestViewModel model)
        {
            return View(model);
        }
    }
}

We always return the view resolved by ASP.NET MVC. By convention this is the view with the same name as the action within the folder with the same name as the controller.

我们总是返回ASP.NET MVC解析的视图。按照惯例,这是与文件夹中具有与控制器同名的操作同名的视图。

This approach works very well for us. If you would like to add the AntiForgeryToken, this also works fine.

这种方法对我们非常有效。如果你想添加AntiForgeryToken,这也可以正常工作。

#2


0  

You should create the main Index view having two partial view , like this:

您应该创建具有两个局部视图的主索引视图,如下所示:

You can define main view model like this

您可以像这样定义主视图模型

public class MainViewModel
{
    public LoginViewModel LoginModel { get; set; }

    public RegisterViewModel RegisterModel { get; set; }
}

then the separate model:

那么单独的模型:

 public class RegisterViewModel
{
 // Your model properties
   }

public class LoginViewModel
{
  // Your model properties  
}

then define your action for main view as:

然后将主视图的操作定义为:

 public ActionResult MainView()
    {

        MainViewModel model = new MainViewModel
        {
            LoginModel = new LoginViewModel(),
            RegisterModel = new RegisterViewModel()
        };

        return View(model);
    }

Your main view as

你的主要观点是

 @Html.Partial("_Login", Model.LoginModel)
  @Html.Partial("_Register", Model.RegisterModel)

after that you can saperately create your views ,thanks

之后你可以单独创建你的观点,谢谢

#3


0  

Sitecore Habitat does it similarly as above, but using a unique rendering ID.

Sitecore Habitat与上面类似,但使用唯一的渲染ID。

public class ValidateRenderingIdAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var ignoreCase = StringComparison.InvariantCultureIgnoreCase;

        var httpRequest = controllerContext.HttpContext.Request;
        var isWebFormsForMarketersRequest = httpRequest.Form.AllKeys
          .Any(key => key.StartsWith("wffm", ignoreCase) && key.EndsWith("Id", ignoreCase));

        if (isWebFormsForMarketersRequest)
        {
            return false;
        }
        string renderingId;
        if (!httpRequest.GetHttpMethodOverride().Equals(HttpVerbs.Post.ToString(), ignoreCase) || string.IsNullOrEmpty(renderingId = httpRequest.Form["uid"]))
        {
            return true;
        }

        var renderingContext = RenderingContext.CurrentOrNull;
        if (renderingContext == null)
        {
            return false;
        }

        Guid id;
        return Guid.TryParse(renderingId, out id) && id.Equals(renderingContext.Rendering.UniqueId);
    }
}

Link to repo

回购回购

#1


13  

Unfortunately there are multiple ways how you can integrate with Sitecore MVC and Sitecore doesn't provide many best practice examples. One example you can find here.

不幸的是,有多种方法可以与Sitecore MVC集成,而Sitecore没有提供许多最佳实践示例。你可以在这里找到一个例子。

In our projects we do it a bit different, because we want to use as much as possible the conventions etc. from default ASP.NET MVC. I try to include a complete simple example in this post.

在我们的项目中,我们做的有点不同,因为我们希望尽可能多地使用默认ASP.NET MVC中的约定等。我试着在这篇文章中包含一个完整的简单例子。

We add two different hidden fields to the form with the current action and the current controller, the view looks like this:

我们使用当前操作和当前控制器向表单添加两个不同的隐藏字段,视图如下所示:

@model Website.Models.TestViewModel

@using (Html.BeginForm())
{
    @Html.LabelFor(model => model.Text)
    @Html.TextBoxFor(model => model.Text)

    <input type="submit" value="submit" />

    <input type="hidden" name="fhController" value="TestController" />
    <input type="hidden" name="fhAction" value="Index" />
}

With this simple ViewModel:

有了这个简单的ViewModel:

namespace Website.Models
{
    public class TestViewModel
    {
        public string Text { get; set; }
    }
}

Then we have created a custom attribute which checks if the current controller/action is the same as posted:

然后我们创建了一个自定义属性,用于检查当前控制器/操作是否与发布时相同:

public class ValidateFormHandler : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var controller = controllerContext.HttpContext.Request.Form["fhController"];
        var action = controllerContext.HttpContext.Request.Form["fhAction"];    

        return !string.IsNullOrWhiteSpace(controller)
            && !string.IsNullOrWhiteSpace(action)
            && controller == controllerContext.Controller.GetType().Name
            && methodInfo.Name == action;
    }
}

The controller actions then gets the new attribute:

然后控制器操作获取新属性:

namespace Website.Controllers
{
    public class TestController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        [ValidateFormHandler]
        public ActionResult Index(TestViewModel model)
        {
            return View(model);
        }
    }
}

We always return the view resolved by ASP.NET MVC. By convention this is the view with the same name as the action within the folder with the same name as the controller.

我们总是返回ASP.NET MVC解析的视图。按照惯例,这是与文件夹中具有与控制器同名的操作同名的视图。

This approach works very well for us. If you would like to add the AntiForgeryToken, this also works fine.

这种方法对我们非常有效。如果你想添加AntiForgeryToken,这也可以正常工作。

#2


0  

You should create the main Index view having two partial view , like this:

您应该创建具有两个局部视图的主索引视图,如下所示:

You can define main view model like this

您可以像这样定义主视图模型

public class MainViewModel
{
    public LoginViewModel LoginModel { get; set; }

    public RegisterViewModel RegisterModel { get; set; }
}

then the separate model:

那么单独的模型:

 public class RegisterViewModel
{
 // Your model properties
   }

public class LoginViewModel
{
  // Your model properties  
}

then define your action for main view as:

然后将主视图的操作定义为:

 public ActionResult MainView()
    {

        MainViewModel model = new MainViewModel
        {
            LoginModel = new LoginViewModel(),
            RegisterModel = new RegisterViewModel()
        };

        return View(model);
    }

Your main view as

你的主要观点是

 @Html.Partial("_Login", Model.LoginModel)
  @Html.Partial("_Register", Model.RegisterModel)

after that you can saperately create your views ,thanks

之后你可以单独创建你的观点,谢谢

#3


0  

Sitecore Habitat does it similarly as above, but using a unique rendering ID.

Sitecore Habitat与上面类似,但使用唯一的渲染ID。

public class ValidateRenderingIdAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var ignoreCase = StringComparison.InvariantCultureIgnoreCase;

        var httpRequest = controllerContext.HttpContext.Request;
        var isWebFormsForMarketersRequest = httpRequest.Form.AllKeys
          .Any(key => key.StartsWith("wffm", ignoreCase) && key.EndsWith("Id", ignoreCase));

        if (isWebFormsForMarketersRequest)
        {
            return false;
        }
        string renderingId;
        if (!httpRequest.GetHttpMethodOverride().Equals(HttpVerbs.Post.ToString(), ignoreCase) || string.IsNullOrEmpty(renderingId = httpRequest.Form["uid"]))
        {
            return true;
        }

        var renderingContext = RenderingContext.CurrentOrNull;
        if (renderingContext == null)
        {
            return false;
        }

        Guid id;
        return Guid.TryParse(renderingId, out id) && id.Equals(renderingContext.Rendering.UniqueId);
    }
}

Link to repo

回购回购