如何在 Umbraco 7.5.8 中 post 自定义模型到控制器?

How to post a custom model to a controller in Umbraco 7.5.8?

我在 CMS 内容树中有一个文档类型、模板和一个页面,该页面将它们用于联系人页面。文档类型没有 CMS 数据属性,因为它不需要任何数据属性。我对其他页面使用模型生成器没有问题,但对于这个页面,我在我的 MVC 项目中创建了我自己的自定义模型。

我已经阅读了我能找到的所有教程,并查看了 Umbraco 论坛和 Whosebug 上的每个论坛 post 和问题,但我终究无法弄清楚自己在做什么做错了。模型名称和命名空间与自动生成的模型构建器不冲突。

我的理解是 posting 表单 SurfaceController 是可行的方法 - RenderController 更适合呈现内容。所以我的控制器扩展了 SurfaceController。使用 Umbraco.BeginUmbracoForm(等等)

我已经尝试了 SurfaceController 和 RenderController 与 UmbracoTemplatePage、UmbracoViewPage 的每一种组合,以及改变我的模型以扩展 RenderModel 和 IPublishedContent 的每一种方式来测试它们。在尝试 RenderController 时,我用 RenderModel 参数覆盖了默认的 Index 方法,以使用 renderModel 参数创建我的模型实例。

通常我得到的错误是"Cannot bind source type Umbraco.Web.Models.RenderModel to model type xxx"。有时我尝试的组合允许 Get 成功,然后在 Post.

上给出此错误

我什至尝试从 CMS 中删除页面并使用标准 MVC 控制器和路由 - 这允许我显示页面,甚至在我的视图中使用标准 Html.BeginForm,我得到尝试 post 表单时出错(尽管控制器中的代码中有断点被击中),它也说明了 "Cannot bind source type Umbraco.Web.Models.RenderModel to model type xxx"

这不可能这么难。我准备在这个阶段扔掉笔记本电脑 window。

我做错了什么????或者至少在没有看到我的代码的情况下,谁能告诉我这 应该 是如何完成的?您如何 post Umbraco 7.5 控制器的自定义模型表单,而不需要 CMS 发布的内容属性?

就目前而言,我的视图如下所示:

@inherits UmbracoViewPage<Models.Contact>
... 
using (Html.BeginUmbracoForm<ContactController>("Contact", FormMethod.Post

我的控制器看起来像这样:

public class ContactController : SurfaceController
{
    public ActionResult Contact()
    {
        return View("~/Views/Contact.cshtml");
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Contact(Contact contactForm)
    {
        ...
    }

我的模型是这样的:

public class Contact : RenderModel
{
    public Contact() : base(UmbracoContext.Current.PublishedContentRequest.PublishedContent, UmbracoContext.Current.PublishedContentRequest.Culture)
    {

    }

    public Contact(IPublishedContent content) : base(content, CultureInfo.CurrentUICulture) 
    {

    }

    [Display(Name = "First Name", Prompt = "First Name")]
    public string FirstName { get; set; }

...

更新: 如果我将模型用于由模型构建器自动创建的 CMS 页面,Get 和 Post 工作正常。但是,当我自定义模型时(即我在 ~/App_Data/Models 中放置了同名的部分 class 并在 Developer 选项卡上重新生成模型),我的 posted 模型中的自定义属性总是空。

我可以从请求表单变量中手动填充这些,但这似乎是错误和混乱的。这是怎么回事?

public class ContactPageController : SurfaceController
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Contact(ContactPage contactForm)
    {
        try
        {
            contactForm.FirstName = Request.Form["FirstName"];
            contactForm.LastName = Request.Form["LastName"];
            contactForm.EmailAddress = Request.Form["EmailAddress"];
            contactForm.Telephone = Request.Form["Telephone"];
            contactForm.Message = Request.Form["Message"];

            var captchaIsValid = ReCaptcha.Validate(ConfigurationManager.AppSettings["ReCaptcha:SecretKey"]);

            if (ModelState.IsValid && captchaIsValid)
            {
                // Do what you need
                TempData["EmailSent"] = true;

                return RedirectToCurrentUmbracoPage();
            }

            if (!captchaIsValid)
            {
                ModelState.AddModelError("ReCaptchaError", "Captcha validation failed - Please try again.");
            }

            return RedirectToCurrentUmbracoPage();
        }
        catch (Exception ex)
        {
            LogHelper.Error(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, null, ex);
            return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
        }
    }

更多信息: 罗伯特的方法第一次,谢谢。我曾尝试过一种使用 PartialViews 和 ChildActions 的方法,但显然我没有正确地做到这一点。我说完全需要这种方法的原因是否正确(即为什么你不能将自定义属性添加到 main view 绑定到的模型中)是因为我正在使用模型生成器?

所以在我收到的奇怪错误中有一个大约 2 classes 都想表示联系页面,另一个关于它期望字典(ContactPage)中的一种 class 但接收另一个 (Contact) - 即使我没有在视图或控制器中引用 ContactPage。这向我建议 ModelsBuilder 在应用程序启动时在幕后添加文档类型到模型的映射?这也许就是为什么你最好采用这种让 ModelsBuilder 做它的事情的方法,并以这种方式在局部视图之上构建你自己的模型?

我发现关于这个主题的文档质量确实很差。不确定好东西是否是闭门造车,即需要付费的 Umbraco 会员资格?对于一个所谓的开源系统,我觉得有点阴暗。

当你知道怎么做时很容易!!

对于您的情况,SurfaceController 是您推测的最有可能的候选者,但是请将该控制器中的操作视为应用于部分视图,而不是页面使用的完整视图。

不要尝试使用 ModelsBuilder ContactPage 模型,而是创建您自己的原始模型(原始 Contact 模型)- 将其视为数据传输对象,如果出于任何原因,您确实需要将任何属性应用回 ContactPage 模型。

我发现我在 SurfaceController 方面取得了最大的成功,条件如下:

  1. 页面模板没有继承自表单的模型;它直接继承自标准 Umbraco PublishedContentModel 或 ModelsBuilder 生成的模型。
  2. 为您的 SurfaceController 中定义的操作实施分部视图 - 此视图继承自示例中的 Umbraco.Web.Mvc.UmbracoViewPage<Contact>
  3. 局部视图利用 BeginUmbracoForm<ContactController>,将 POST 操作指定为参数之一(取决于您使用的签名)

您不需要像这样使用 Request.Form 填充任何模型属性。

例如,我的一个项目中的代码如下所示:

表面控制器

public class FormsController : SurfaceController
{
    [ChildActionOnly]
    public ActionResult ContactUs()
    {
        return PartialView(new ContactForm ());
    }

    [HttpPost]
    public async Task<ActionResult> HandleContactUs(ContactForm model)
    {
        if (ModelState.IsValid)
        {
            if (!await model.SendMail(Umbraco)) // Do something with the model.
            {

            }
        }

        return RedirectToCurrentUmbracoPage(); // Send us back to the page our partial view is on
    }

}

局部视图:

@inherits Umbraco.Web.Mvc.UmbracoViewPage<ContactForm>
@using Digitalsmith.ReCaptcha
@using (Html.BeginUmbracoForm<FormsController>("HandleContactUs"))
{
    ...
}

联系页面模板:

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContactPage>
@{
    Layout = "_Layout.cshtml";
}
@Html.Action("ContactUs", "Forms")