views:

72

answers:

2

I am making my own base controller since I want to pass some data to a master page. Now since this is like adding this code to every view in that controller it gets run every time.

By the time it loads up the first time I think it has hit my code at least twice. So I was thinking about caching it. But in my book it says don't cache private data since everyone will see it.

So I am not sure what to do.

What my couple lines of code does is this.

  1. Finds the user name and displays it to the user.
  2. Find the users plan and displays that.

So I need the userName to find out what their GUID so I can find out what plan they signed up for.

So I don't know how to cache it but not expose it to everyone. Is there away to make it cache just for this user?

Quote Asp.net mvc framework unleasehd pg 330

Don't Cache Private Data

Caching a page that contains private user data is extremely dangerous. When a page is cached on the server, the same page is served to all users. So, if you cache a page that displays a user credit card number, everyone can see the credit card number (not good!).

The same page is cached for all users even when you require authorization. Imagine, for example, that you create a financial services website and the website includes a page that displays all a user's investments. The page displays the amount of money that a user has invested in a set of stocks, bonds, and mutual funds.

Because this information is private, you require the user to log in before seeing the page. In other words, each user must be authorized before the user can view the page.

To improve the performance of the financial services page, you decide to enable caching for the page. You add the OutputCache attribute to the controller action that returns the page.

Don't do that! If you cache the controller action that returns the financial services pages, the private financial data for the first person to request the page will be shared among every subsequent visitor to the application.

In general, adding the OutputCache and Authorize attribute to the same controller action opens a security hole. Avoid doing this:

+1  A: 

Your Controller classes only have a life cycle that stretches from action-request to view-rendered. Do you really need to cache that data ? Just expose the data in your Model object. The queries don't sound complex enough and on top of that most RDBMS vendors tend to do a lot of query caching themselves.

HTTP is a stateless protocol meaning that after a request your web server forgets everything about said request. To circumvent this statelessness asp.net offers the Application and Session objects. Application is globally available to all requests and Session is bound to a single IP. Putting any data in there is usually a last resort though or part of schemes to cache data that truly have a heavy load time.

The flow in ASP.NET MVC is roughly

  • URL is requested
  • Controller is called
  • Action is called
  • Action calls Model for data (username/userplan)
  • Action sets the data in the viewmodel
  • Action calls the desired view with the data in the viewmodel
  • View is rendered.

Setting the data on the viewmodel is already 'private' so I am not sure where the need for caching comes from. Perhaps I am misunderstanding the question ?

Edit

Ok first forgive me if my post was a bit presumptuous. I do think your code doesn't have to go in the constructor of your base constructor at all. You don't pass your controller to the view after all.

A good practice is to pass in typed ViewModels. Say this is your definition in your site.master

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

And this the definition of say your Help page View:

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

You can then create a BaseViewDataModel

namespace MyNS
{
    public class BaseViewDataModel

And your HelpViewDataModel

namespace MyNS
{
    public class HelpViewDataModel : BaseViewDataModel 
    {

Then in your help controller's appropiate action

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

You can now instantiate your user guid and user plan in BaseViewDataModel's constructor and it will only be called for actions that actually instantiate a BaseViewDataModel subclass.

And your views are type safe which is huge productivity gain since we typed the views we can now actually call

<%= this.Model.HelpText %>

In the Help's View.

Martijn Laarman
To pass in data to a master page without putting this code in every actionView in my controller. I have to make my own baseController. This is where my queries stick. So on initialize in my base controller I have a query that gets the userPlan. Every time. My page where I am trying to show this code is all ajax that goes to views and has ajax tabs. Every time you click on a tab will cause me rehitting that query. So if no caching is done this method will keep hitting the db for no reason. And I am not even looked if my ajax requests that go to partialView to return segments of data will...
chobo2
cause this code to also be hit. It could be that the user does 10 actions on that page and that query gets hit 10 times. Now think 50 times? 100 times. So if I could get it so that it only needs to really do this query once and cache it then that would be great. Looking at your life cycle flow this code would be called somewhere in the middle of Url and Controller(whenever the master page gets called basically).
chobo2
I updated my answer to better reflect where I am coming from .
Martijn Laarman
+2  A: 

The best way to answer whether you should cache something or not is the determine if it's actually impacting the performance of your site. With actual metrics. Don't guess. If it's not hurting anything and everything seems responsive, then I would say work on something else until the need arises. "Permature optimization is the root of all evil" has been said a time or two :)

If you do choose to cache data, I'm unsure what you mean that everyone will be allowed to see it? If you mean the ASP.NET Caching framework (HttpContext.Current.Cache) then this lives in your server memory and is completely volatile (it would be wiped out at any time if memory pressure hits it). So make sure you keep that in mind and always check for null. To use it, you can simply use Cache.Insert() and it has a couple overloads to customize your caching preferences.

Now if you are talking about caching something with browser cookies that is another story. Cookies are indeed stored in the browser, and unless you specify HttpOnly for the cookie, it can be read via javascript (which is only a problem if you have an XSS vulnerability somewhere -- because a malicious user can use javascript to "phone-home" users' private cookie data.) So don't put anything private into a cookie unless absolutely necessary, and if you do, you should specify HttpOnly and take appropriate steps to protect your users.

You should definitely read more about XSS and other common HTTP security issues if security is a big concern for you. (And it should be :)

UPDATED to address the question edit regarding Outout Caching:

Ok so you were specifically referring to Output Caching. Indeed that book mentions a security hole that I believe was patched before MVC 1 was released. The Authorize attribute should be smart enough to correctly handle Output Cacheing when these 2 attributes are used in conjunction. Perhaps that book was written during a preview release and is no longer relevant.

I am quoting the following from Steve Sanderson (author of Pro ASP.NET MVC Framework)

Update: Since the Beta release, the [Authorize] filter now does some clever trickery to co-operate with ASP.NET output caching. Specifically, it registers a delegate using HttpCachePolicy.AddValidationCallback(), so that it can intercept future cache hits and tell ASP.NET output caching not to use the cache when [Authorize] would reject the request. This solves the problem of ASP.NET output caching bypassing the [Authorize] filter. If you’re going to write your own authorization filter, be sure to derive it from AuthorizeAttribute so you can inherit this useful behaviour.

Note that this doesn’t stop ASP.NET output caching from bypassing any of your other action filters, and it doesn’t add any support for partial caching. If that’s a problem for you then consider using [ActionOutputCache] (below) instead.

It would be worthwhile to read his article (and his book, for that matter): http://blog.codeville.net/2008/10/15/partial-output-caching-in-aspnet-mvc/

Matt Hidinger
Good to note that HttpOnly is not supported by all browsers.
Martijn Laarman
See my edit above.
chobo2
Updated post to address your updated concern for output caching.
Matt Hidinger
Hmm this book is one of the newest books out their. Released July 24th,2009. So what 2 months old?
chobo2
It might be worthwhile to note that output caching would only work for (almost) static data which a User GUID and User plan doesn't sound like they are (as they're also request specific).
Martijn Laarman
Ya they are specific to the user but so I am trying to find out if there is a way to cache it for that specific user. So a million queries for the same thing is not needed.
chobo2
Firstly if you follow my BaseViewModel concept your only loading it when needed (i.e not for ajax requests). Secondly if your still concerned about doing to many request you can store the data in the Session object which is client specific. But measure first before going through such lengths as Matt Hidinger quite rightly mentions.
Martijn Laarman