带回发的MVC组件

时间:2016-01-21 10:55:21

标签: c# asp.net asp.net-mvc components child-actions

我正在尝试构建一组MVC组件,可以通过各种设置轻松重用。它们被构建并用作子操作,因为我需要它们能够在不知道托管它们的视图的其他内容的情况下对它们自己进行部分回发。

我面临的问题是如何存储他们的参数而不通过客户端传递它们(我不想构建像视图状态,也出于安全原因),并使它们可用于部分回发。

这是一个简化的示例(并不是代码可能无法编译,因为我削减了我的语法糖,以简化它为简单的MVC):

查看代码(组件用法):

@Html.Action(
    "Default",
    "FacebookFeed",

    new {
        // I don't want this data to pass through client
        settings = new FacebookFeedSettings {
            AppKey = "XYZ",
            AppSecret = "123",
            PageSize = 10
        }
        .ItemTemplate(
            @<div class="feed-item">@item.Title</div>
        )
    }
)

控制器代码:

public class FacebookFeedController {
   public ActionResult Default(FacebookFeedSettings settings)
   {
      // Action code using settings

      return PartialView(model);
   }
}

Feed视图代码:

@using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }))
{
    // Form code

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

因此,当点击“刷新”按钮时,它应该呈现新数据,但该请求中缺少设置。

到目前为止,我只提出了解决方案,使用字符串键编制某种设置寄存器,然后注册设置。

修改后的视图代码(组件用法):

@Html.Action(
    "Default",
    "FacebookFeed",
    new {
        settingsKey = "HomePageFBFeed"
    }
)

来自用户的额外代码:

[ComponentSettings]
public class HomePageFBFeed : FacebookFeedSettings
{
    public HomePageFBFeed()
    {
        AppKey = "XYZ";
        AppSecret = "123";
        PageSize = 10;
    }
}

修改后的控制器代码:

public ActionResult Default(string settingsKey)
{
   FacebookFeedSettings settings = ComponentSettings.GetSettings(settingsKey);

   // Action code using settings

   return PartialView(model);
}

修改后的视图代码:

@using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }, new { settingsKey = Model.SettingsKey }))
{
   ...
}

所以在这种情况下我只通过客户端传递该配置的一些唯一ID,这很好,但与第一次相比它具有糟糕的用户体验,因为它需要在放置组件的视图之外进行管理。

在这种情况下,我也无法使用内联模板,如第一个代码部分所示,因为在这种情况下,设置是在视图范围之外构建的。

请注意,我还需要使用它来处理应用程序重新启动和跨进程边界(在云端),因此我不能依赖于在第一次加载视图时在服务器端存储配置。

在ASP.NET 4.6 / MVC 5中有没有更好的方法/最佳实践?

如果没有,是否可以在ASP.NET 5 / MVC 6中使用?

2 个答案:

答案 0 :(得分:0)

Martin,我知道你说你不想创建一个视图状态,但考虑到MVC的断开连接模式(你不想使用服务器端Session,也因为它不能扩展),您是否可以转移加密的设置字符串?

视图中的内容如下:

@using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }))
{
    @Html.AntiForgeryToken()
    <input type="hidden" name="Settings" value="@Model.ToEncryptedString()" />
    <input type="submit" value="Refresh" />
}

ToEncryptedString()将是您模型的扩展,即:

  1. 序列化您的设置(FacebookFeedSettings对象)
  2. 加密
  3. 转换为Base 64以使其成为HTTP友好
  4. 返回控制器时,您需要做的就是阅读默认操作中的Settings参数:

    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult Default(string settings)
    {
        FacebookFeedSettings facebookSettings = FacebookFeedSettings.FromEncryptedString(settings);
        // do something with the settings
        // build the model
        return PartialView(model);
    }
    

    FromEncryptedString()完全相反:

    1. 从Base 64转换为byte []
    2. 解密字节数组
    3. 反序列化为FacebookFeedSettings对象实例
    4. 基本上,这个加密的字符串或多或少与防伪标记一样。 为了使它更优雅,我想您也可以在属性级别移动设置验证,并使用自定义属性标记您的操作:

      [HttpPost, ValidateAntiForgeryToken, FacebookFeedSettings]
      public ActionResult Default(/* No need to capture settings here */)
      

      FacebookFeedSettingsAttribute将从Request和构建中获取Settings参数并验证FacebookFeedSettings对象实例。 在我的尝试中我没有尝试过这么远,我留给你练习: - )

      您怎么看?

答案 1 :(得分:0)

我明白并且我同意,问题是你无论如何都要保持某种形式的国家,采用无国籍技术。

好消息是,MVC 6似乎可以解决您的问题。首先,子动作不再存在,取而代之的是View Components。 视图组件由C#类和Razor视图组成,并且不依赖于Controller,这样可以减轻可重用性。

我还没有直接经验,但从我正在阅读的有关MVC 6,特别是View Components的内容来看,它们是独立的,所以基本上你可以在组件本身内管理状态。参数不是通过HTTP从视图传递的,因为您实际上是调用组件服务器端(不从客户端返回)。

此外,View组件不参与控制器生命周期,但您仍然可以访问ViewBag和ViewData(与Controller共享)。最后,与控制器一样,View Components也参与依赖注入,因此您需要的任何其他信息都可以简单地注入到视图组件中。

这应该对你有用,但你需要等待MVC 6: - )