如何在 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
方面取得了最大的成功,条件如下:
- 页面模板没有继承自表单的模型;它直接继承自标准 Umbraco PublishedContentModel 或 ModelsBuilder 生成的模型。
- 为您的 SurfaceController 中定义的操作实施分部视图 - 此视图继承自示例中的
Umbraco.Web.Mvc.UmbracoViewPage<Contact>
。
- 局部视图利用
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")
我在 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
方面取得了最大的成功,条件如下:
- 页面模板没有继承自表单的模型;它直接继承自标准 Umbraco PublishedContentModel 或 ModelsBuilder 生成的模型。
- 为您的 SurfaceController 中定义的操作实施分部视图 - 此视图继承自示例中的
Umbraco.Web.Mvc.UmbracoViewPage<Contact>
。 - 局部视图利用
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")