【问题标题】:MVC Surface Controller & Umbraco Current NodeMVC 表面控制器和 Umbraco 当前节点
【发布时间】:2012-04-20 14:06:55
【问题描述】:

我正在尝试在表面控制器中编写一个子动作函数,该函数被宏调用以呈现 PartialView。

我需要在这个函数中访问我当前的页面属性,然后调整渲染的 PartialView。

我从 Jorge Lusar 的 ubootstrap 代码中得到了这个,它在 HttpPost ActionResult 函数上运行良好:

var renderModel = (UmbracoRenderModel)ControllerContext.RouteData.DataTokens["umbraco"];
var currentPage = renderModel.CurrentNode.AsDynamic();

问题是我在 [ChildActionOnly] PartialViewResult 函数上抛出了这个错误:

Unable to cast object of type 'System.String' to type 'Umbraco.Cms.Web.Model.UmbracoRenderModel'.
on 'var renderModel = (UmbracoRenderModel)ControllerContext.RouteData.DataTokens["umbraco"];'

DataTokens["umbraco"] 中的数据似乎在两个函数之间发生变化。 如果我在每一个上显示 DataTokens["umbraco"].ToString(),会发生以下情况:

[ChildActionOnly] public PartialViewResult Init() -> "Surface" 上显示。

[HttpPort] public HandleSubmit(myModel model) -> 显示“Umbraco.Cms.Web.Model.UmbracoRenderModel”

在这里感谢您的任何建议。

尼古拉斯。

【问题讨论】:

    标签: asp.net-mvc umbraco


    【解决方案1】:

    我正在使用 Umbraco 6.0.4,它很简单:

    var currentNode = Umbraco.TypedContent(UmbracoContext.PageId);
    

    【讨论】:

    • 这在 SurfaceController 中不起作用。 UmbracoContext.PageId 为空。
    • 如果它为空,那么你不在页面上!!
    • 如果是表面控制器,我不在页面嘿嘿
    • SurfaceController 需要从节点渲染(我的意思是从模板使用:Html.Action("ActionName", "MySurface") 或使用:using (Html.BeginUmbracoForm("PostAction", "MySurface")) 发布它)
    【解决方案2】:

    当我们丢失 uformpostroutevals 的隐藏值时,我在 Surface Controller 中获取当前节点 ID 时遇到了同样的问题。

    即使我试图发布这个值,通过从表单中获取它,由呈现

    @using (Html.BeginUmbracoForm("<ActionName>", "<Controller Name>Surface"))

    我在UmbracoContext 的所有属性中仍然有 null,所以看起来它没有正确初始化。

    HOTFIX:我将 CurrentNodeId 传递给我通过 Ajax 发送的每个表单:

    在一般母版页中,我正在创建全局 javascript 对象:

    <script type="text/javascript">
      var Global = {
        //..List of another variables which can be usefull of frontend
        currentNodeId: @CurrentPage.Id
      };
    </script>
    

    在任何请求中,都可以轻松地将Global.currentNodeId 用作data 的参数之一:

    var sendData = {
        currentNodeId: Global.currentNodeId,
        // another params 
    };
    
    $.ajax({
        method: 'POST',
        data: JSON.stringify(sendData),
        contentType: 'application/json; charset=utf-8',
        url: '/Umbraco/Surface/<ControllerName>Surface/<ActionName>',
        dataType: 'json',
        cache: false
    })
    

    请注意,这只是一个热修复而不是正确的解决方案!

    【讨论】:

      【解决方案3】:

      要访问您的 currentPage,您需要在 Controller 中实现此构造函数

      public class CommentSurfaceController : SurfaceController
      {
          private readonly IUmbracoApplicationContext context;
          public CommentSurfaceController(IUmbracoApplicationContext context)
          {
              this.context = context;
          }
      }
      

      它使用 umbracos 依赖注入,来解析 Context 依赖,并使其可供你使用。

      查看有关 SurfaceController 的文档 https://github.com/umbraco/Umbraco5Docs/blob/5.1.0/Documentation/Getting-Started/Creating-a-surface-controller.md

      【讨论】:

      • 听起来不错。但我必须将 nodeID 作为宏参数传递?这意味着如果我想要当前页面,在我的富文本编辑器中插入时,我仍然要在宏的 ocntent 选择器参数中选择当前节点?!没有其他直接的方法吗?
      • 如果您需要当前的 currentPage,您可以使用控制器中的“this.CurrentPage”。这为您提供了当前的配置单元节点。
      • System.InvalidOperationException:在 Umbraco.Cms.Web.Surface.SurfaceController.get_CurrentPage() 中使用 BeginUmbracoForm 帮助程序时,只能在 Http POST 的上下文中使用 UmbracoPageResult
      【解决方案4】:

      虽然我更愿意找到一种更简单的方法,但我想我已经找到了一个可行的解决方案。关键是区分操作方法是作为子操作(通常通过 HTTP GET)还是直接(通过 HTTP POST)调用的。

      以下是一个自定义基类,它公开了一个“CurrentContent”属性,然后可以通过继承表面控制器来使用。

      using System.Web.Mvc;
      using Umbraco.Cms.Web;
      using Umbraco.Cms.Web.Surface;
      using Umbraco.Cms.Web.Model;
      
      namespace Whatever
      {
          public abstract class BaseSurfaceController : SurfaceController
          {
              private object m_currentContent = null;
      
              public dynamic CurrentContent
              {
                  get
                  {
                      if (m_currentContent == null)
                      {
                          if (Request.HttpMethod == "POST")
                          {
                              m_currentContent = GetContentForSubmitAction();
                          }
                          else
                          {
                              m_currentContent = GetContentForChildAction();
                          }
                      }
                      return m_currentContent;
                  }
              }
      
              // from Lee Gunn's response
              // http://our.umbraco.org/forum/core/umbraco-5-general-discussion/29178-In-a-controller-how-do-I-get-the-current-pages-hiveId?p=2
      
              private object GetContentForChildAction()
              {
                  ViewContext vc = ControllerContext.RouteData.DataTokens[
                          "ParentActionViewContext"] as ViewContext;
                  var content = vc.ViewData.Model
                          as global::Umbraco.Cms.Web.Model.Content;
                  return content.AsDynamic();
              }
      
              // from Nicholas Ruiz
              // http://our.umbraco.org/forum/core/umbraco-5-general-discussion/30928-Surface-Controller-and-Current-Node-Properties
      
              private object GetContentForSubmitAction()
              {
                  UmbracoRenderModel rm =
                          ControllerContext.RouteData.DataTokens["umbraco"] as UmbracoRenderModel;
                  if (rm == null)
                  {
                      return GetContentForChildAction();
                  }
                  return rm.CurrentNode.AsDynamic();
              }
          }
      }
      

      不过,似乎应该有一种更简单的方法来做到这一点。

      布莱恩

      【讨论】:

        【解决方案5】:

        在 Umbraco 7.2.1 中,我使用 ChildActionOnly 属性并将模型从父级传递给局部视图。

                [ChildActionOnly]
            public ActionResult InitializeDataJson(KBMasterModel model)
            {
                var pluginUrl = string.Concat("/App_Plugins/", KBApplicationCore.PackageManifest.FolderName);
                bool isAuthenticated = Request.IsAuthenticated;
                IMember member = null;
                if (isAuthenticated)
                    member = UmbracoContext.Application.Services.MemberService.GetByUsername(User.Identity.Name);
                var data = new { CurrentNode = model.IContent, IsAuthenticated = isAuthenticated, LogedOnMember = member, PluginUrl = pluginUrl };
                var json = JsonConvert.SerializeObject(data, Formatting.None, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
                var kbData = new TLCKBData() { InitializationJson = json };
                return PartialView(kbData);
            }
        

        现在部分视图代码:

        @model TLCKBData
        <script>
            (function () {
                var data = JSON.parse('@Html.Raw(Model.InitializationJson)');
                tlckb.init(data);
            })();
        </script>
        

        以及渲染子动作的父视图:

        @section FooterScript {
            @{Html.RenderAction("InitializeDataJson", "KBPartialSurface", new { model = Model });}
        }
        

        注意:我正在使用强模型,因为我已经为我正在开发的插件路由劫持了我的所有文档类型,但是如果我正在路由劫持并且只使用 UmbracoTemplatePage 模型(在 Umbraco 中默认),那么我将更改参数 on我的孩子只采取 RenderModel 或 UmbracoTemplatePage 的行动。

        然后我会以同样的方式将模型传递给它。

        因为它是表面控制器上的子动作,所以已经加载到 Index 中的模型只是传递给子动作。这可以防止 GetContent 代码在管道中运行两次。

        我这样做的原因是我需要一些基本数据来初始化我的 Angular API 层。比如它是否经过身份验证,登录的成员是谁等。最终我在那里有一些标签,类别等。

        我还想尽可能高效地构建插件,并且没有多余的逻辑。我认为所有信息都在主视图模型上,为什么我必须再次查找它?就在那时,我想出了如何做到这一点。

        【讨论】:

          【解决方案6】:
          var currentNode = Umbraco.TypedContent(UmbracoContext.PageId);
          

          或者,如果您在表面控制器中有对象

          var currentNode = CurrentPage;
          

          (转到定义)

           //
           // Summary:
           //     Gets the current page.
           protected virtual IPublishedContent CurrentPage { get; }
          

          确保您首先检查 null,因为在某些情况下您可以在没有解析 currentPage 上下文的情况下调用操作。

          【讨论】:

            【解决方案7】:

            这里有一些东西帮助我克服了这个问题。

            在 jquery 包含之后,我添加了一个自定义标题标签,如下所示。

                <script type="text/javascript">
                    $.ajaxSetup({
                        headers: { 'umbraco-page-id': '@CurrentPage.Id' }
                    });
                </script> 

            现在每个 jquery 帖子都会跨越当前的 umbraco 页面。您可以从请求标头属性中访问此自定义标头。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-05-25
              • 1970-01-01
              相关资源
              最近更新 更多