1

我正在尝试构建一组 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);
   }
}

提要视图代码:

@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 中使用?

4

2 回答 2

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 友好

返回控制器时,您需要做的就是读取 Default 操作中的 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 对象实例

基本上,这个加密字符串或多或少类似于防伪令牌。为了使其更加优雅,我想您也可以在属性级别移动设置验证,并使用自定义属性标记您的操作:

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

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

你怎么看?

于 2016-01-22T00:56:09.360 回答
0

我理解并且我同意,问题是您无论如何都希望在无状态技术中保留某种形式的状态。

好消息是 MVC 6 似乎可以解决您的问题。首先,子action不再存在,取而代之的是View Components。视图组件由一个 C# 类和一个 Razor 视图组成,并且不依赖于控制器,从而简化了可重用性。

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

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

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

于 2016-01-24T20:58:06.473 回答