I'm trying to prevent output cached page from being ejected from my asp.net output cache unless another one can replace it. In other words, if a page has expired (based on Duration property of OutputCache profile) but accessing the page now results in an exception, I just want to serve the old, outdated page. I was hoping the custom cache providers in ASP.NET 4 would help my situation but they don't get far enough up the pipeline.
Here's what I know:
System.Web.Caching.OutputCache is really hard to read through
A custom cache provider hooks in after all dependencies and is fed a CachedVary object.
The utcExpiry date passed into the custom cache provider's Add and Set methods is just DateTime.Max
The actual expiration of the cache item happens higher up in the pipeline
Even for expired items, the custom cache provider's Get method is still called. It returns the cached CachedVary object and then my controller's action method is called. After this, the Add method is called with the CachedVary object and Set method is passed an actual cached page data object.
Can someone with more experience hacking outputcache steer my in a direction here? Can I accomplish what I'm looking for with ASP.NET outputcache? Must I roll my own?
i ended up blogging about how i accomplished this here: http://statichippo.com/blog/archive/2011/09/25/graceful-degradation-via-asp-net-outputcacheprovider.aspx
Related
We have a web app that displays data from a couple of different web apps. In each of those web apps, the culture is configurable (it is set by a company admin at the app level for all users, not per user).
In our web app, we need to display the data from each of the other web apps, in the culture configured for that app. The only good news is that currently we don't display "mixed" data from both apps, in a single view. And generally, a controller and all of its actions will only work with data from a single app (meaning that all actions from that controller will use the same culture).
There are several things I'm struggling with:
Does .NET Core really not honor setting culture like in the old days? I've tried it but it doesn't work (unless I'm setting it in the wrong place in the pipeline).
Thread.CurrentThread.CurrentCulture = firstWebAppCultureInfo;
Thread.CurrentThread.CurrentUICulture = firstWebAppCultureInfo;
Does it really only check these to find and set the culture?
QueryStringRequestCultureProvider
CookieRequestCultureProvider
AcceptLanguageHeaderRequestCultureProvider
I know I could write a custom provider, but that wouldn't seem to help me. We already get the various culture settings for the apps, from a database and store them in session.
My first thought was to write out a Culture Cookie every time the user hits a controller. I'd read the culture from session and write the cookie. But there are a couple of issues I see with this.
Issue 1: If I write the cookie out in the controller action, it doesn't "take affect" the first time it is changed from one culture to another. Obviously the cookie has already been read and the culture has already been set for that request. I'm too late in the pipeline to do that.
I tried writing the cookie out in the constructor of the controller (or constructor of a base controller) but HttpContext is not yet set at that point (it is null).
I have worked a bit with Filters and I will write an Action Filter that writes the cookie to see if that works, but see Issue 2.
Issue 2: Another issue would surface if the user opened multiple instances of their browser. The last culture written will "win" I suspect. So if a user is displaying data in Culture fr-FR in one browser and en-US in another instance of a browser and navigates to a controller/view that writes that en-US cookie, and then from the other browser instance navigates to a page that is supposed to use the fr-FR culture, the user would get the en-US setting from the cookie and that's not acceptable.
I don't really want to use the QueryStringRequestCultureProvider, because having that show up in the url is ugly.
I'm definitely not an expert in this area.
What am I missing?
How would you all implement this?
Thanks in Advance.
UPDATE
I created an ActionFilter and TypeFilterAttribute that allows me to pass a parameter into the ActionFilter. Using the parameter I can decide which key to read from Session to give me the culture setting I've stored for each web app.
From there I can mark each controller with that attribute and the appropriate parameter to tell the action filter what culture to use (remember we don't mix data, so each controller displays views that show data from only one of the two web apps, not both).
In the ActionFilter I can look up the culture string from session and do the following and it works:
var ci = new CultureInfo(this.cultureSetting);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
While that is encouraging, it feels hacky. So please comment on the solution...
I'd like to find a way to do all of this in a BaseController or somewhere, so I wouldn't need to put attributes on all of our controllers.
But whatever I've tried in a base controller seems like it is either:
a) too late in the pipeline to set the culture for that request, or
b) too early in the pipeline to get access to httpcontext.
Thoughts? Ideas?
UPDATE 2
I guess overriding OnActionExecuting in my base controller lets me do the same things as the ActionFilter...
So I guess I have a solution, but if anyone has a better idea, I'm listening.
We decided to go with an ActionFilter for our solution, so we can mark Controllers and/or Actions with an Attribute that indicates which app we are getting data from, so we can apply that app's culture setting...
I'm finding that in the current application I'm working with, I'm retrieving several entities (related to the authenticated users account) in almost every controller. These entities are cached at the orm layer however, it seems that these entities would be a good candidate to load once at authentication time and add a few properties to the applications custom IPrincipal object.
Another option I was thinking of was creating a custom context object (with the users related account objects) and passing it around with the current request.
Benefits / drawbacks to either approach? Is there another way of dealing with commonly used objects like this?
It sounds like you miss the fact that the instance of IPrincipal/IIdentity is recreated upon every request. It is not persisted anywhere if you not persist it in an explicit way.
I don't think then there's performance difference between a custom principal class holding the data vs a cached ambient property.
On the other hand, the drawback of a custom authentication classes is that you have to provide a custom authentication module so that these instances are recreated during AuthenticateRequest event in the processing pipeline. In other words, you'd have to replace FormsAuthenticationModule with your own one. This is not difficult but I wouldn't do this if it is not absolutely necessary.
Note also that some data can be persisted in the UserData section of the forms cookie. This means that you can have it as long as the cookie is valid and create it only once.
I'm new to this AJAX approach when you're not supposed to use UpdatePanel but rather go with WebMethods and WebServices. My main problem is authentication.
When page is entered or postback request is created, everything works fine. Authentication is passed correctly and I can access Thread.CurrentPrincipal.Identity and get my user identity object from there.
However this changes when I try to call WebMethod on my page. Call is passed correctly to server and everything seems to work just fine until i try to get user identity from thread. Then I get just Anonymous user instead of real one. Enabling session on webmethod didn't seem to help much.
Any ideas what might cause this problem and how to solve it? Someone mentioned that authentication cookie needs to be passed along with the request, but how am I supposed to do it?
Any help will be appreciated.
Edit:
Some clarification and code:
My application is written in standard asp.net. After some deeper research in legacy code I've found out, that all authentications are done in some base class from wchich all other pages inherit. Each time page is loaded, user principal are obtained from HttpContext.Current.Session("..."). I think this is far from good solution, but I'll need to stick with it right now. Problem was, WebMethod is not firing whole page lifecycle since it's static. I've fixed it right now by calling method that obtains user data from session.
I would like to get some ideas how this could be created correctly and what problems might be result of session based authentication.
PageMethods.SomeMethod(parameter, SuccessDelegate, FailureDelegate);
This is how I'm calling WebMethods right now. I assume it's passing all required cookies, am I right?
It depends on how you're calling the method and in what manner?
Jquery for instance with its Post method should push all cookies (including your FormsAuth / Session cookie) up with the request that should still authenticate as appropriate. Bare metal techniques might be making lightweight calls that simply do not push the cookie up...One way to monitor this is by using Fiddler to observe the request and a browser based development plugin like Firebug and see what is occuring and amend your JS code as appropriate.
Personally, if you are starting a brand new project and there is no pressing need to expose your services beyond your web application then I would suggest looking at ASP.NET MVC where you can make Jquery / client-side up to the controller and get your authentication wrapped up for free. I've recently created something simliar using WCF JSON endpoints and some inevitable pain, I then saw MVC and kinda kicked myself...
As noted in comment above, the issue lies in legacy code that handles users. It is needed to make call to special function that assigns appropriate user data to handling thread. Not a best solution, but that's how it sometimes is with legacy code. What you gonna do?
I have an ASP.NET application which requires output caching. I need to invalidate the cached items when the data returned from a web service changes, so a simple duration is not good enough.
I have been doing a bit of reading about cache dependencies and think I have the right idea. It looks like I will need to create a cache dependency to my web service.
To associate the page output with this dependency I think I should use the following method:
Response.AddCacheItemDependency(cacheKey);
The thing I am struggling with is what I should add to the cache?
The dependency my page has is to a single value returned by the web service. My current thinking is that I should create a Custom Cache Dependency via subclassing CacheDependency, and store the current value in the cache. I can then use Response.AddCacheItemDependency to form the dependency.
I can then periodically check the value and for a NotifyDependencyChange in order to invalidate my cached HTTP response.
The problem is, I need to ensure that the cache is flushed immediately, so a periodic check is not good enough. How can I ensure that my dependant object in the cache which represents the value returned by the web service is re-evaluated before the HTTP response is fetched from the cache?
Regards, Colin E.
I believe you are on the right track with your cache dependency. However, if you don't "periodically check" the return value of the web service, how can you know when it returns a new value? You may need to set up a web service in the other direction, so that when the value changes in the other system, it can call your system and invalidate the old cache and stick in the new value.
You can manually invalidate a cached page using:
System.Web.HttpResponse.RemoveOutputCacheItem(path)
I want to implement an ISAPI filter like feature using HttpModule in IIS7 running under IIS Integrated Request Processing Pipeline mode.
The goal is to look at the incoming request at the Web Server level, and inject some custom HttpHeaders into the request. (for ex: HTTP\_EAUTH\_ID)
And later in the page lifecycle of an ASPX page, i should be able to use that variable as
string eauthId = Request.ServerVariables["HTTP\_EAUTH\_ID"].ToString();
So implementing this module at the Web Server level, is it possible to alter the ServerVariables collection ??
HttpRequest.ServerVariables Property is a read-only collection. So, you cannot directly modify that. I would suggest storing your custom data in httpcontext (or global application object or your database) from your httpmodule and then reading that shared value in the aspx page.
If you still want to modify server variables, there is a hack technique mentioned in this thread using Reflection.
I believe the server variables list only contains the headers sent from the browser to the server.
You won't be able to modify either the HttpRequest.Headers or the HttpRequest.ServerVariables collection. You will however be able to tack on your information to any of:
HttpContext.Current.Items
HttpContext.Current.Response.Headers
Unfortunately, Request.Params, Request.QueryString, Request.Cookies, Request.Form (and almost any other place you'd think of stuffing it is read only.
I'd strongly advise against using reflection if this is a HttpModule you're planning on installing into IIS 7. Given that this code will be called for (potentially) every request that goes through the web server it'll need to be really fast and reflection just isn't going to cut it (unless you have very few users).
Good luck!