缓存还是不缓存?如何在asp.net mvc的基础控制器上进行缓存?

4
我正在制作自己的基本控制器,因为我想向主页面传递一些数据。由于这就像在该控制器的每个视图中添加此代码,所以它每次都会运行。
第一次加载时,我认为它至少已经命中了我的代码两次。所以我考虑缓存它。但是在我的书中,它说不要缓存私有数据,因为每个人都会看到它。
所以我不确定该怎么办。
我的几行代码所做的是:
1.查找用户名并向用户显示它。 2.查找用户计划并显示它。
所以我需要userName来查找他们的GUID,以便我可以找出他们注册的计划。
所以我不知道如何缓存它,但不向所有人公开它。是否有方法使其仅为此用户缓存?
引用Asp.net mvc framework unleasehd pg 330
不要缓存私有数据 缓存包含私有用户数据的页面非常危险。当页面在服务器上缓存时,相同的页面将提供给所有用户。因此,如果您缓存显示用户信用卡号的页面,则每个人都可以看到信用卡号(不好!)。
即使您要求授权,也会为所有用户缓存相同的页面。例如,假设您创建了一个金融服务网站,该网站包括显示用户所有投资的页面。该页面显示用户投资一组股票、债券和共同基金的金额。
由于此信息是私有的,因此您需要在查看页面之前要求用户登录。换句话说,每个用户必须经过授权才能查看页面。
为了提高金融服务页面的性能,您决定为该页面启用缓存。您将OutputCache属性添加到返回页面的控制器操作中。
不要这样做!如果您缓存返回金融服务页面的控制器操作,则第一个请求页面的人的私有财务数据将在随后的访问者中共享应用程序。
通常,将OutputCache和Authorize属性添加到同一控制器操作会打开安全漏洞。避免这样做:
2个回答

2
回答是否应该缓存某些内容的最佳方法是确定它是否实际影响了您的站点性能,使用实际指标来判断,不要猜测。如果没有影响并且一切都响应良好,那么我会建议在需要时再处理其他事情。 "过早的优化是万恶之源"已经说过很多次 :)
如果您选择缓存数据,我不确定您所说的每个人都可以看到它的意思是什么?如果您指的是ASP.NET缓存框架(HttpContext.Current.Cache),则它位于服务器内存中并且完全不稳定(如果内存压力达到,则会被清除)。因此,请确保牢记这一点并始终检查null。要使用它,只需使用Cache.Insert(),它有几个重载以自定义缓存首选项。
现在,如果您要缓存浏览器cookie中的某些内容,那就是另一回事了。 Cookie确实存储在浏览器中,除非您为cookie指定了HttpOnly,否则可以通过javascript读取(只有在某个地方存在XSS漏洞时才是问题--因为恶意用户可以使用javascript“phone-home”用户的私有cookie数据)。因此,除非绝对必要,否则不要将任何私人内容放入cookie中,并且如果确实需要,请指定HttpOnly并采取适当的措施保护您的用户。
如果安全是您关注的重点,那么您应该阅读更多有关XSS和其他常见HTTP安全问题的内容。(而且应该如此:)
更新以回答有关输出缓存的问题:
好的,所以您特别提到了输出缓存。确实,该书提到了一个安全漏洞,我认为在MVC 1发布之前已被修补。当这两个属性同时使用时,Authorize属性应聪明地处理输出缓存。也许该书是在预览版发布期间编写的,现在已经不再相关。
我引用了Steve Sanderson(Pro ASP.NET MVC Framework的作者)的以下内容:
更新:自Beta版本以来,[Authorize]过滤器现在会做一些巧妙的技巧,以与ASP.NET输出缓存协作。具体来说,它使用HttpCachePolicy.AddValidationCallback()注册委托,以便它可以拦截未来的缓存命中并告诉ASP.NET输出缓存在[Authorize]拒绝请求时不要使用缓存。这解决了ASP.NET输出缓存绕过[Authorize]过滤器的问题。如果您将编写自己的授权过滤器,请务必从AuthorizeAttribute派生它,以便可以继承此有用的行为。
请注意,这并不会阻止ASP.NET输出缓存绕过您的其他操作过滤器,并且它不添加任何对部分缓存的支持。如果这对您造成问题,请考虑改用[ActionOutputCache](下面)来代替。
这篇文章(以及他的书)值得一读: http://blog.codeville.net/2008/10/15/partial-output-caching-in-aspnet-mvc/。它介绍了在 ASP.NET MVC 中如何进行部分输出缓存。

值得注意的是,输出缓存仅适用于(几乎)静态数据,而用户GUID和用户计划似乎不是静态数据(因为它们还与请求相关)。 - Martijn Laarman
是的,它们是针对用户特定的,但我正在尝试找出是否有一种方法可以为该特定用户缓存它。因此,不需要进行一百万次相同的查询。 - chobo2
首先,如果你遵循我的 BaseViewModel 概念,你只有在需要时加载它(即不用于 Ajax 请求)。其次,如果你仍然担心执行太多请求,你可以将数据存储在客户端特定的 Session 对象中。但是像 Matt Hidinger 所提到的那样,在采取这些措施之前先进行度量。 - Martijn Laarman

1

你的控制器类只有从请求操作到视图渲染的生命周期。你真的需要缓存那些数据吗?只需在模型对象中公开数据即可。查询似乎并不复杂,而且大多数关系型数据库管理系统供应商 tend to do a lot of query caching themselves。

HTTP 是一种无状态协议,这意味着在请求之后,您的 Web 服务器会忘记有关该请求的所有内容。为了规避这种无状态性,asp.net 提供了 Application 和 Session 对象。Application 对于所有请求都是全局可用的,而 Session 则绑定到单个 IP。但是,将任何数据放入其中通常是最后的选择或者是缓存具有重载时间的数据方案的一部分。

ASP.NET MVC 的流程大致如下:

  • 请求 URL
  • 调用控制器
  • 调用操作
  • 操作调用模型获取数据(用户名/用户计划)
  • 操作将数据设置在视图模型中
  • 操作使用视图模型中的数据调用所需的视图
  • 渲染视图。

已经在视图模型上设置了数据,因此我不确定缓存的需求来自何处。也许我误解了问题?

编辑

首先请原谅,如果我的帖子有点自以为是。我认为您的代码根本不必放在基础构造函数的构造函数中。毕竟,您没有将控制器传递给视图。

一个很好的做法是传入已命名的ViewModels。 假设这是您在site.master中的定义

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyNS.BaseViewDataModel>" %>

这是你的帮助页面视图的定义:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyNS.HelpViewDataModel>" %>

您可以创建一个BaseViewDataModel。
namespace MyNS
{
    public class BaseViewDataModel

还有你的HelpViewDataModel

namespace MyNS
{
    public class HelpViewDataModel : BaseViewDataModel 
    {

然后在您的帮助控制器的适当操作中

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
    var viewDataModel = new HelpViewDataModel ();
            viewDataModel.HelpText = "something";
            ..
            ..
    return View(viewDataModel);
}

现在您可以在BaseViewDataModel的构造函数中实例化用户guid和用户计划,并且它将仅针对实际实例化BaseViewDataModel子类的操作进行调用。

而且,由于我们为视图键入了类型,因此您的视图是类型安全的,这是巨大的生产力提升,因为我们现在可以实际调用它们。

<%= this.Model.HelpText %>

在帮助视图中。


为了在不将此代码放入控制器中的每个actionView中的情况下向主页面传递数据,我必须制作自己的baseController。这就是我的查询所在的地方。因此,在我的baseController中初始化时,我有一个获取userPlan的查询。每次都是如此。我尝试显示此代码的页面是所有ajax,它们转到视图并具有ajax选项卡。每次单击选项卡都会导致我重新访问该查询。因此,如果没有进行缓存,此方法将继续无故地访问数据库。而且,我甚至没有查看我的ajax请求是否转到partialView以返回数据段... - chobo2
导致这段代码被执行的原因。可能是用户在该页面上执行了10个操作,所以查询被执行了10次。现在想象一下50次?100次。所以,如果我能让它只需要执行这个查询一次并进行缓存,那就太好了。根据您的生命周期流程来看,这段代码将在Url和Controller之间的某个位置被调用(基本上是当主页面被调用时)。 - chobo2
我更新了我的回答以更好地反映我的观点。 - Martijn Laarman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接