HttpContext.Response.Cache equivalent in .NET Core? - asp.net

I'm trying to reproduce something I found here for a previous version of ASP.NET.
Basically, I want to be able to disable cache so my client's look to the server for information at all times. I've added an HTML meta tag for this, but for client's that already have this information, I wanted to experiment with handling cache policy on the back-end.
The post mentions doing this to set a cache policy as an action filter.
public class NoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
base.OnResultExecuting(filterContext);
}
}
However, HttpContext doesn't appear to have a Response.Cache in ASP.NET Core. Is there an alternative way of doing this?
Thanks!

You could directly set the corresponding response headers to the desired values:
public class NoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
filterContext.HttpContext.Response.Headers["Expires"] = "-1";
filterContext.HttpContext.Response.Headers["Pragma"] = "no-cache";
base.OnResultExecuting(filterContext);
}
}

You can control it with build-on attribute:
[ResponseCache (NoStore = true, Location = ResponseCacheLocation.None)]

Related

MVC4 Request headers

I have rather bizarre behaviour in my MVC web application.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class NoCacheFilterAttribute : ActionFilterAttribute
{
ctx.Response.AddHeader("Cache-Control", "no-cache);
}
I am adding this filter class to GlobalFilterCollection so that it runs on every action.
I am attempting to override this header selectively in one of my home controller action method.
Response.Headers.Remove("Cache-Control");
Response.AddHeader("Cache-Control", "private,must-revalidate,proxy-revalidate ");
Problem is non these code are setting the correct values. When I inspect the values in response header the Cache-Control is set to "private, s-maxage=0". I scanned all my code to see if I was explicitly doing so anywhere but I don't see myself doing that. The odd thing is If I call
Response.Cache.SetNoStore(); or Any methods of Response.Cache. the response value of Cache-Control then changes. I am not sure why Response.AddHeader or AppendHeader doesn't work?
The "Cache-Control" response header in ASP.NET is special and shouldn't be set explicitly. Instead, use the first-class APIs hanging off of the Response.Cache object as you've already found.
Could you solve it by adding some conditional logic before performing ctx.Response.AddHeader("Cache-Control", "no-cache)?
Basically, you could create another filter named [ApplyCache] to apply to your action method for which you don't want no-cache set, and in NoCacheFilterAttribute, you can do this,
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class NoCacheFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext ctx)
{
if (!ctx.ControllerContext.ControllerDescriptor.GetFilters().Contains(new ApplyCache()) && !ctx.ActionDescriptor.GetFilters().Contains(new ApplyCache())
{
ctx.Response.Headers.Add("Cache-Control", "no-cache");
}
}
}
For MVC this could be,
public class ApplyCache: System.Web.Mvc.ActionFilterAttribute
{
}
public class NoCache: System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
if (!actionExecutedContext.ActionDescriptor.IsDefined(typeof(ApplyCache), true))
{
actionExecutedContext.RequestContext.HttpContext.Response.AddHeader("Cache-Control", "no-cache");
}
}
}
and you want to add both filters to your global collection.
public static void RegisterMvcFilters(GlobalFilterCollection filters)
{
filters.Add(new ApplyCache());
filters.Add(new NoCache());
}
OK. This is what I found out. Any time anywhere in your code if you use (any other NuGet library may be setting the headers) following code:
Response.Cache.SetNoStore(); //or any other .SetXXX; methods
, the headers are written to buffer, and if you issue say following code (some examples using this in ActionFilterAttribute):
HttpContext ctx = HttpContext.Current;
ctx.Response.AppendHeader("Cache-Control", "private, must-revalidate, proxy-revalidate ");
ctx.Response.AddHeader("Cache-Control", "private, must-revalidate, proxy-revalidate ");
ctx.Response.Headers.Set("Cache-Control", "no-cache, no-store, must-revalidate, proxy-revalidate");
to modify the header, the header values are not written. Only way to reset the header is to issue following line of code and then the issue the code above or call Response.Cache.SetXXX methods.
ctx.Response.ClearHeaders();
The Response.Cache does not provide a mechanism to reset the response headers values that were set using .SetXXXX methods. If Response.Cache.SetXXX method was never called then calling Response.AppendHeader, AddHeader or Headers.Set will work without any need to call Response.ClearHeaders();

How to bufferise the response from the action?

How can I send some part of the web response without waiting the all rendering process finish?
I prefer do that in an Action.
I used to use Response.Buffer = false; in .net 2.0 but now with MVC 3 I don't know how to do that.
You can potentially use an Action Filter to set the Buffer property.
public class BufferActionFilter: ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Buffer = true;
}
}
PS: I have not checked if this works though.

Best way to abort/cancel action and response from ActionFilter

Best way to abort/cancel action from ActionFilter
I've got this ActionFilter, and it's suppose to end the connection immediately and return a 401 Unauthroized:
public class SignInRequired : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// User is verified, continue executing action
if (Acme.Web.CurrentUser != null)
{
return;
}
// End response with 401 Unauthorized
var response = HttpContext.Current.Response;
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.End();
// Prevent the action from actually being executed
filterContext.Result = new EmptyResult();
}
}
I learned how you can cancel the action from executing by setting 'context.Result = new EmptyResult()` here, but I'm not sure if this is the best way to flush the response and close the connection.
Setting the response will mean the action doesn't get called.
public override void OnActionExecuting(HttpActionContext actionContext)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
As other answers have said, though, authentication should be done with an AuthorizeAttribute (Docs for Web.API or for MVC).
On .net core 2.2, 3.0 and 3.1 and .net 5 the below example works fine
public override void OnActionExecuting(ActionExecutingContext context)
{
context.Result = new UnauthorizedObjectResult("user is unauthorized");
}
The answer that #OdeyinkaOlubunmi is correct for Web API or specifically System.Web.Http.Filters.ActionFilterAttribute but it can't be used for System.Web.Mvc.ActionFilterAttribute. AuthorizeAttribute and overriding AuthorizeCore is a good way to go but if you use #Vadim's example for a GlobalFilter you will end up with the following error in a standard configuration:
HTTP Error 404.15 - Not Found The request filtering module is
configured to deny a request where the query string is too long.
This is because the default /Login?ReturnUrl= will keep appending new values until the query string causes an exception.
The way I have solved it for MVC is like this:
public class DebugActionFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext actionContext)
{
actionContext.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
return;
}
}
You can set the result of filterContext for the Exception page like this:
filterContext.Result = new RedirectResult("~/Error/Unauthorized");
See more details here on section Canceling Filter Execution
You probably want to make it an AuthorizeAttribute. That will set the result to be an UnAuthorizedResult automatically, plus it has the benefit of being run before any other filters. Alternatively you can set the Result to be a new HttpUnauthorizedResult
public class SignInRequiredAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return !Acme.Web.CurrentUser != null;
}
}
using .net core 2.1 the solutions above did not work for me , so i tried this and it worked :-
context.HttpContext.Response.StatusCode = 401;
return;
if there is better solutions for .net core 2.1 i am open for suggestions

ASP.NET MVC - compression + caching

I've seen a number of options for adding GZIP/DEFLATE compression to ASP.Net MVC output, but they all seem to apply the compression on-the-fly.. thus do not take advange of caching the compressed content.
Any solutions for enabling caching of the compressed page output? Preferably in the code, so that the MVC code can check if the page has changed, and ship out the precompressed cached content if not.
This question really could apply to regular asp.net as well.
[Compress]
[OutputCache(Duration = 600, VaryByParam = "*", VaryByContentEncoding="gzip;deflate")]
public ActionResult Index()
{
return View();
}
Use caching options using attributes (for MVC), and do not think about compression since IIS/IISExpress automatically compresses your output if you enable it.
the way it works, mvc does not enable caching of individual fragments or parts of output (partial content caching). if you want this, consider using a service like CloudFlare (is there any other like CF?). it automatically caches your output and caches fragments of your output and provides many other performance and security improvements, all without a change in your code.
If this is not an option for you, then you still may use IISpeed (it is a IIS port of Google's mod_pagespeed). It provides some interesting settings like whitespace removal, inline css and js compression, js file merge and many other.
Both CF and IISpeed does not care how your site is built, they work on http/html level, so they both work on MVC, Classic ASP.NET, php or even raw html files.
You can create a attribute like
public class EnableCompressionAttribute : ActionFilterAttribute
{
const CompressionMode Compress = CompressionMode.Compress;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpRequestBase request = filterContext.HttpContext.Request;
HttpResponseBase response = filterContext.HttpContext.Response;
string acceptEncoding = request.Headers["Accept-Encoding"];
if (acceptEncoding == null)
return;
else if (acceptEncoding.ToLower().Contains("gzip"))
{
response.Filter = new GZipStream(response.Filter, Compress);
response.AppendHeader("Content-Encoding", "gzip");
}
else if (acceptEncoding.ToLower().Contains("deflate"))
{
response.Filter = new DeflateStream(response.Filter, Compress);
response.AppendHeader("Content-Encoding", "deflate");
}
}
}
Add entry in Global.asax.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new EnableCompressionAttribute());
}
Then you can use this attribute as:
[EnableCompression]
public ActionResult WithCompression()
{
ViewBag.Content = "Compressed";
return View("Index");
}
You can download working example from Github:
https://github.com/ctesene/TestCompressionActionFilter
This link seems fairly close to what you require. It caches compressed dynamically generated pages. Although the example uses Web forms, It can be adapted to MVC by using an OutputCache attribute
[OutputCache(Duration = 600, VaryByParam = "*", VaryByContentEncoding="gzip;deflate")]
You could create a Cache Attribute:
public class CacheAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
if (Enabled)
{
cache.SetExpires(System.DateTime.Now.AddDays(30));
}
else
{
cache.SetCacheability(HttpCacheability.NoCache);
cache.SetNoStore();
}
}
public bool Enabled { get; set; }
public CacheAttribute()
{
Enabled = true;
}
}
See Improving performance with output caching for a full introduction on the subject. The main recommendation is to use the [OutputCache] attribute on the Action to which caching should be applied.
use namespace
using System.Web.Mvc;
using System.IO.Compression;
create ClassName.cs in you main project
public class CompressAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var _encodingsAccepted = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
if (string.IsNullOrEmpty(_encodingsAccepted)) return;
_encodingsAccepted = _encodingsAccepted.ToLowerInvariant();
var _response = filterContext.HttpContext.Response;
if(_response.Filter == null) return;
if (_encodingsAccepted.Contains("deflate"))
{
_response.AppendHeader("Content-encoding", "deflate");
_response.Filter = new DeflateStream(_response.Filter, CompressionMode.Compress);
}
else if (_encodingsAccepted.Contains("gzip"))
{
_response.AppendHeader("Content-encoding", "gzip");
_response.Filter = new GZipStream(_response.Filter, CompressionMode.Compress);
}
}
}
--- and add in global.asax.cs
GlobalFilters.Filters.Add(new CompressAttribute());

Disable Session state per-request in ASP.Net MVC

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)
Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.
I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)
So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!
If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller
[SessionState(SessionStateBehavior.ReadOnly)]
See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.
Thanks to https://stackoverflow.com/a/4235006/372926
Rather than implementing an action filter for this, why don't you implement a RouteHandler?
Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.
If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.
This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.
I also came across the same problem and after doing R&D this link worked for me
Reference:
https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
Create custom Attribute
Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
Register it in global.aspx
1> Create custom Attribute
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior SessionBehavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
{
SessionBehavior = sessioBehavior;
}
}
2. Override
public class SessionControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return SessionStateBehavior.Default;
var actionName = requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
// [Line1]
var cntMethods = controllerType.GetMethods()
.Where(m =>
m.Name == actionName &&
( ( typeOfRequest == typeof(HttpPostAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
)
||
( typeOfRequest == typeof(HttpGetAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
)
)
);
MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
if (actionMethodInfo != null)
{
var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (sessionStateAttr != null)
{
return sessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
3. Register class in Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// --- other code ---
ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
}
}
Try serving the images from another domain. So something like images.mysite.com.
This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.
Have you considered setting up a HttpHandler to serve up your images?
SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.
Idea is to tell the asp.net that specific request wont use session object.
So, Create a custom route handler for specific requests
public class CustomRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
return new MvcHandler(requestContext);
}
}
SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.
After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax
routes.Add("Default", new Route(
"{controller}/{action}",
new RouteValueDictionary(new { controller = "Home", action = "Index"}),
new CustomRouteHandler()
));
Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.
Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.
1.Set web.config/system.web/sessionState:mode="Off".
2.create DictionaryTempDataProvider class.
public class DictionaryTempDataProvider : ITempDataProvider
{
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
return new Dictionary<string, object>();
}
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
}
}
3.Create DictionaryTempDataControllerFactory
public class DictionaryTempDataControllerFactory : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName) as Controller;
if (controller!=null)
controller.TempDataProvider = new DictionaryTempDataProvider();
return controller;
}
}
4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
new DictionaryTempDataControllerFactory()
);
}
On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.
Is it possible that your ASP.NET app is serving the files instead of IIS?
Create new Controller
Decorate controler with [SessionState(SessionStateBehavior.Disabled)]
Refactor code you want seesion stated disabled for to that controller
I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += OnPostMapRequestHandler;
}
private void OnPostMapRequestHandler(object sender, EventArgs e)
{
var context = (sender as HttpApplication).Context;
DisableSessionForSomeRequests(context);
}
private void DisableSessionForSomeRequests(HttpContext context)
{
if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
{
context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{ }
}

Resources