How to use OutputCacheLocation.ServerAndClient without Vary: * HTTP header - c#

I am trying to use OutputCache for both server and client caching on an MVC view but setting Location to ServerAndClient forces the HTTP header Vary: * to be set, which largely defeats the purpose of the client caching (it tells the browser to check for freshness EVERY time the resource is used).
I want the browser to cache the file for 24hours and only make another request if I change the LastModifield parameter that I add to the querystring. This works if I have only Client as the location but I also want the server to cache the file so that it doesn't need to regenerate it when another user requests the same resource.
The resource is based on a database that will rarely get updated (possibly once or twice a month, I know when it has changed) and the resource could get hit very hard so I don't want to be generating it all the time or even handling modified-since conditionaly requests.
My OutputCache attribute is:
[OutputCache(Location = OutputCacheLocation.ServerAndClient, Duration = 86400, VaryByParam = "LastModified")]
I have tried extending the OutputCacheAttribute class and overriding all of the On* methods and removing the Vary http header but the Vary header doesn't seem to be added until AFTER all of these methods have been called.

Response.Cache.SetOmitVaryStar(true);

Related

Seeking source of Http Response Headers in MVC application

I've inherited a .NET C# MVC application. I noticed that different pages interact differently with the server when the user uses the Back and Forward buttons built into the browser. Some pages would hit the server and some would not. I used Postman to hit the various URLs and found that different pages are returning different response headers.
Cache-Control: private
Cache-Control: public, no-store, max-age=0
Pages in the first set are cached in the browser's "private" cache. There is no hit against the server when this page is loaded in the browser via the Back or Forward button.
The second control string is somewhat of a conflict, however the most restrictive directive of no-store overrides. These pages hit the server every time the page is loaded.
I've searched the codebase. I've located related meta tags in various page template markup files but I'm looking specifically for the headers. Moreover, the meta tags are all the same, so that could not account for the different headers in different pages.
IIS is not configured to add this header. Moreover, when I run this application in debug, out of Visual Studio / IIS Express, I see these headers sent back with the response. Different headers for different pages.
I can't find any explicit code which is emitting these headers on the server (I searched for Response.AddHeader and didn't find anything) so I'm thinking that there might be different configurations of the different MVC Templates which is implicitly generating these headers? Does that make sense? (I don't have a lot of experience with MVC.) I'll keep looking, but if you have knowledge of MVC which could point me in the right direction, I'd really appreciate that.
I continued looking into this. As Alexei Levenkov pointed out, Angular is executing client side. Not the right place to be looking.
Here's an image of a Debug session processing the page request. At this point there are exactly two headers in the Response.Headers collection.
After this statement executes, the response received by the browser contains many more headers. The View() method is not my code so I have no ability to trace into it. Clearly though, the View() method reacts to some declarative configuration somewhere which results in different response headers for different templates. As I mentioned before, I'm no Angular.js expert and I'm not much better with MVC. I'm probably missing something very basic.
Within this transaction there is nothing which executes after the return View() statement. Step over that statement, step off the closing method brace and the content returns to the client.
Postman shows 11 response headers, including the one in particular which interests me.
Now here I am stepping into a different request which returns a different Cache-Control header. Again, we see the same two headers in the Response.Headers collection.
Yet, the View method returns 13 response headers and a different Cache-Control header:
Here's a clue: The different pages which return different headers are grouped into different controllers. One controller returns Cache-Control: private, while the other controller returns Cache-Control: public, no-store, max-age=0.
I suppose, ultimately, the question boils down to the following: How are controllers defined in MVC to return specific headers on the response? (Like I said, I'm not an MVC expert so this might be a very long-winded presentation to get to a very basic question. Thanks for your help!)
I believe this is the answer. It's a meta-tag on the Controller. (As a declarative, this would not be seen when stepping through the code. An examination of the source is necessary and of course, it helps to know what you are looking for. Now I know!)
namespace rater8.RMM.Web.Controllers
{
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class RMMController : Controller
{
...
}
...
}
The other controller did not have any such meta-tag. It appears that not specifying this meta-tag results in Cache-Control: private, which allows browsers to preserve the page in the local cache for Back and Froward navigation without hitting the server.
Specifying the meta-tag as shown in the code example above results in Cache-Control: public, no-store, max-age=0 (That's a strange cache-control value where public is in conflict with no-store but the latter wins out as it is the more restrictive attribute.)

OutputCacheAttribute - does it vary on HTTP method?

I have a following method in my controller:
[AcceptVerbs("GET", "OPTIONS", "HEAD")]
[OutputCache(Duration="3600" VaryByParam="None" VaryByHeader="Access-Control-Request-Headers;Origin")]
public new ActionResult Index()
{
//action body
}
It is handling both GET and OPTIONS (CORS) calls and so far there has been no problem. I've recently added the OutputCache attribute and started wondering if it's possible to cache a flawed OPTIONS response, by calling the GET. Namely, let's say a user with malicious intents calls my GET call with the CORS headers that I vary on. Is it possible that a user making the OPTIONS call with the same headers (this time used properly), will instead get the response from the previously cached GET, therefore nuking the whole OPTIONS call for the duration of the cache?
I was looking for an information, if the HTTP method is considered when creating an output cache entry, but just can't find it anywhere. I have tested this locally and it seemed that the GET and OPTIONS output could never get mixed up, no matter how hard I messed with the headers. Still, I would be much more relieved if I knew that what I described could really never happen.

What's the best way to implement cache busting in a .NET project based on DLL changes?

I'm looking to implement cache busting for our JavaScript and CSS based on code changes (changes to a DLL) in a .NET project by adding ?v=number to the end of the link/script tag src paths. I want to do this in the most performant way possible, since this will be used on every page of the application. Would just obtaining the DLL version be the best way to generate that number, as explained here?
Caching is one of the hardest problems in Computer Science. There's no magic answer to fit all problems, because caching is an optimisation technique: you make tradeoffs to achieve the performance profile you're interested in. Only you will know what is "best" for your problem.
For HTTP, there's a series of headers which indicate to clients how they should perform caching. As with all headers, the client may choose to ignore them and do its own thing, but you should be comfortable that most clients will pay attention to what you send back.
The relevant headers to this discussion are:
cache-control
etag
cache-control
This header indicates back to clients what basic caching rules they should apply. If this header is not specified, the client can make its own choice on what to do in regards to caching. If you're not sending this header, you can't make many assumptions about what your clients are doing.
The cache-control header is composed of a number of directives to indicate the caching rules to apply to the resource. The common ones are:
private | public - A private directive indicates that proxy servers should not cache this value; responsibility of caching lies entirely with the client. A public directive indicates that proxy servers may cache this resource. If you are serving resources which are customized for end-users (such as having the user's name somewhere on the page), the private directive is appropriate. If you are serving resources which are shared across all your users, public is appropriate (such as a favicon or logo).
max-age This indicates how many seconds the resource should be cached for before the client goes back for another copy, regardless of any other caching policies. This is the maximum amount of time the resource will remain in the client's cache.
no-cache This tells clients not to cache the resource and to check for a new version every time. This doesn't mean the client not cache the resource at all, but that it will check to see if the resource has changed every time a request is made. The etag header will be relevant here.
no-store This indicates that the client should not store the response at all.
A cache-control value to indicate a dynamic resource (a resource which changes on every request) is:
cache-control: no-cache
This tells the client and any proxy servers that this resource should be checked every time a request is made.
A cache-control header to cache a resource for 1 day looks like this:
`cache-control: public, max-age=86400
etag
The etag header is short for Entity Tag. You can think of the etag header as being like a hash-code for your resource. When you provide this header, you give the client a way to determine whether the resource has changed without having to retrieve the whole resource.
When a client has an etag value for a resource, it can make a request to the server which is like "give me this resource if its etag value is different from the one I have`. You still have the cost of a network round-trip, but your client will only receive the new resource if the value has changed.
The etag header most useful at saving bandwidth. If you are using an etag your clients will only download the new version when it actually changes, and otherwise will cache the value indefinitely. The requests the clients do make are very small and complete more quickly than re-downloading the full resource.
When you combine etag with cache-control, the cache-control header decides when the local cached value is no longer valid, and the etag is used in the subsequent request to see whether the resource has been changed.
Depending on what frameworks and libraries you are using, there are many ways to control these header values, but you will need to make informed guesses to what to set them to.
I would suggest that, where you can cheaply produce one, applying an etag to a response is a simple way to achieve decaching as you have asked for. You should generally combine an etag with a cache-control header that includes a max-age value that is appropriate for how "responsive" you need your clients to be when the values are changed.
As a final note, don't forget that caching is an optimisation. You can turn it off and you should if the cost of caching isn't worth the payoff.

ASP.NET MVC OutputCache does not store Custom Headers

My application uses ASP.NET MVC 5 with OutputCache (in detail, we use MVCDonutCaching) to cache high traffic sites and expensive routes.
Some of the Actions have a Custom ActionFilter which adds a Content-Range header depending on the view model. Without caching it works like charm. When the cache is enabled the first hit is ok (Content-Range header is present in the response) - but the second one only contains Content-Type and the HTML/JSON Response and our custom Content-Range header is missing (which breaks the client functionality).
Is there any way to enable proper header caching without writing an own OutputCache implementation?
Thank you very much.
The cached response is a "304 - Not Modified" HTTP Response, and that kind of response is not expected to return entity headers (except some exceptions like "Last-Modified").
The "Content-Range" header you are trying to return is an entity header:
http://www.freesoft.org/CIE/RFC/2068/178.htm
Here is a full list of Entity headers:
https://www.rfc-editor.org/rfc/rfc2616#section-7.1
The reason why 304 is not returning entity headers is that the 304 response is not supposed to return a full representation of the target resource, since nothing changed.
The 304 (Not Modified) status code indicates that a conditional GET
or HEAD request has been received and would have resulted in a 200
(OK) response if it were not for the fact that the condition has
evaluated to false. In other words, there is no need for the server
to transfer a representation of the target resource because the
request indicates that the client, which made the request
conditional, already has a valid representation;
That means that entity headers should not be transferred again. This ensures consistency, and also has some performance benefits.
If the conditional GET used a strong cache validator (see section 13.3.3), the response SHOULD NOT include other entity-headers. Otherwise (i.e., the conditional GET used a weak validator), the response MUST NOT include other entity-headers; this prevents inconsistencies between cached entity-bodies and updated headers.
https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-p4-conditional-23#section-4.1
My conclusion is that ASP.NET and IIS are interpreting this specification correctly, and what you are trying to do is NOT supported. A prove for that is that Apache, and other popular web servers do the same as explained above.
If you still need that header in your 304 you will have to identify and replace (if possible) the components responsible for filtering the 304 responses.

Best Way to avoid Reinsertion of data in ASP.net on Page Refresh

I want to know what is the best way to avoid the reinsertion of data in ASP.net.
I am currently doing
Response.Redirect('PageURL');
Thanks in Advance
Don't put your insertion code in the Page_Load method, or if you are, make sure you are checking Page.IsPostBack first.
Yes, normally we have an identity autoincrement number id, wich should be sent back to your form after the insertion. So you just have to check on server if that number is > 0 and execute an update instead of an insert.
Your redirect solution is valid. This pattern is called Post/Redirect/Get.
Post/Redirect/Get (PRG) is a web development design pattern that
prevents some duplicate form submissions, creating a more intuitive
interface for user agents (users). PRG implements bookmarks and the
refresh button in a predictable way that does not create duplicate
form submissions.
When a web form is submitted to a server through an HTTP POST request,
a web user that attempts to refresh the server response in certain
user agents can cause the contents of the original HTTP POST request
to be resubmitted, possibly causing undesired results, such as a
duplicate web purchase.
To avoid this problem, many web developers use the PRG pattern[1] —
instead of returning a web page directly, the POST operation returns a
redirection command. The HTTP 1.1 specification introduced the HTTP
303 ("See other") response code to ensure that in this situation, the
web user's browser can safely refresh the server response without
causing the initial HTTP POST request to be resubmitted. However most
common commercial applications in use today (new and old alike) still
continue to issue HTTP 302 ("Found") responses in these situations.
Use of HTTP 301 ("Moved permanently") is usually avoided because
HTTP-1.1-compliant browsers do not convert the method to GET after
receiving HTTP 301, as is more commonly done for HTTP 302.[2] However,
HTTP 301 may be preferred in cases where it is not desirable for POST
parameters to be converted to GET parameters and thus be recorded in
logs.
http://en.wikipedia.org/wiki/Post/Redirect/Get

Categories