MVC OutputCache based on Session values - c#

Is it possible to vary the output cache in MVC based on certain values in the session? I've read a lot about using the varybycustom functionality and overriding GetVaryByCustomString in Global.asax but the session is not available at this point.
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "somekey")
//Want to check the session here (but it isn't available).
return base.GetVaryByCustomString(context, custom);
}
I understand this is because the Session isn't created until later in the request pipeline.
My concern is that without varying the cache based on the user's session, the page (which changes based on what the user has in the session, has additional HTML specific to that user etc) will get cached (as the URL is the same) and served by our load balancer, proxy servers etc. and then served to other requests with other people's session information on the page!
The reason the URL is the same is that the user comes in as a 'guest', enters some information (POST), this is validated and stored in the session and then they are re-directed back to the same page (which should now be specific to the user based on the session data).
The page itself should be cached normally because if a 'guest' visits the same URL, it should serve the same 'standard' page every time.
Is is possible to vary the caching in this way?

If you want to personalize the cache output per user, it is better you set the Location to OutputCacheLocation.Client as below. More information here
[OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
public string GetName()
{
return "Hi " + User.Identity.Name;
}

Would a Output Cache ActionFilter help at all?
Or perhaps you could refactor your view in to a layout page plus partial views for anonymous and authenticated sections, then utilize Partial Caching.

You should look into "Donut Caching", but this isn`t supported by ASP.NET MVC 3, at least not out of the box. Fortunately somebody already solved this problem for you see MvcDonutCaching
I read that ASP.NET MVC 4 will include "Donut Hole Caching" out of the box, but i cant tell if it's in the current RC or not.

Related

Asp.net core 5, using Identity, how do I change the default redirect of the [Authorize] Attribute?

The [Authorize] is wonderful for locking pages down but I am building a new product with few users and it makes no sense that it directs people to Login, because there is no one to login yet. It should direct them to Register instead.
But I am struggling to find an easy way to do that without a ton of middleware.
You can change the LoginPath on start up, but I suspect this does not answer your question because when enough users exists then what happens?
To change the login path you can add:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(cookieOptions =>
{
cookieOptions.LoginPath = "/register";
cookieOptions.AccessDeniedPath = "/account/denied";
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(120);
});
However if you are wanting a switch when you have reached a critical mass then this will not suffice.
EDIT one approach could be something like:
Create a loginOrRegister page. Then on this page hit the database (or whatever you use to see whether you have hit critical mass or its a known user based on a cookie) and then either
Redirect to Login
OR
Rediect to register

Pass and verify data between different razor pages

i'm using Asp.net Core2.1 Razor pages technology.
i want to ask about passing and verifying parameters between razor pages.
please not that i'm asking about the concept not about coding:
now suppose i have blogs, and each blog owned by a user who can manage it.
the user enter the manage page using this url :
https://localhost:44368/blogs/1/settings
as you see, the id of the blog is in the url:
public async Task<IActionResult> OnGetAsync(int? id)
{
// here i check that the blog is exist by the id
// and i check if the current user own the blog
}
then in the settings page i have links for several pages, for example (articles)
and the user can manage these articles.
https://localhost:44368/blogs/1/settings/articles
as you see still i have the blog id in the url :
public async Task<IActionResult> OnGetAsync(int? id)
{
// now this function in the articles page
// again i check if the blog is exist
// and again i check if the current user can manage the blog or not
}
is this correct and good practice ? to check and verify in each page
or should i check only when i enter the settings page ?
or should i think in an approach to check only once when the user enter the settings page, then the user can't enter other pages based on the first check !
It is good practice to keep web endpoints stateless.
So your approach of passing the Id to every child action, and validating this input, is correct.
To implement the other approach where you only check the Id once, you would need to pass state between the actions, e.g. as session state. This approach is less flexible. Maybe you want to be able to open the settings from another page than the blog details someday in the future?
Also remember that just because the user does not see a certain link on a page, nothing prevents her from entering e.g. https://localhost:44368/blogs/1/settings/articles directly into the address bar of the browser. So you will need some from of validation for every action in any case.

Is it possible to have a single URL that points to two different pages?

I have one url (/settings) that needs to point to two different pages depending on the users security on login. One page is the existing webforms page the other is a new MVC page. Is this even possible?
Additional Info:
When the user is on either page the url needs to say website.com/settings
Solution:
Convinced the PM to change the requirements.
The short answer, yes. You can do this several ways.
Javascript
Model View Controller (Controller)
ASP.NET Web-Forms (Method)
It is often poor practice to do such an event, as it can expose data. It is indeed possible:
Javascript:
$(document).ready(function () {
if($("#Account").val() != '') {
$(".Url").attr('href', 'http://www.google.com');
}
});
Pretend #Account is a hidden field that is populated from your database. If the field is not null then modify the .Url element to navigate to link. That approach for Web-Forms is the most simple.
Web-Forms:
protected void btnAccount_Click(object sender, EventArgs e)
{
if(User.IsInRole("Account"))
Response.Redirect("~/Admin.aspx");
else
Response.Redirect("~/User.aspx");
}
That would use the default Windows Authentication for the domain, you could bend and contort to use the database to pull data. An example, the Model View Controller would be similar as the Controller will simply handle that capability.
Hope this points in right direction.
This is a redirects based approach. Create a web page mapped to /settings, and have this code run on page load.
if(User.IsAdministrator()) //I take it you have some way of determining who is an Admin, so this is just example code
{
Response.Redirect("~/AdminSettings.aspx");
}
else
{
Response.Redirect("~/UserSettings.aspx");
}
Note that you'll need security on the Admin page to make sure a regular user can't just navigate directly there.

Clear browser cache only on logout

How can I clear browser cache only on logout, sure I can use the below:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
But this particular page which is a shopping bag page is accessible by both login and non-login users. How can I set it in such a way whereby the login user is able to access this page without clearing the browser cache but Only clears it when he/she logs out so that another user will not be able to access the history contents.
I have tried the solutions here:
http://www.codeproject.com/Tips/135121/Browser-back-button-issue-after-logout
made some changes but still couldn't figure out how to deal with this issue.
I also cleared my session on logout as below but I understand the browser cache will still stay.
FormsAuthentication.SignOut();
Session.Abandon();
Response.Redirect("~/");
Please advice. Thanks.
I am not a c# expert, but I am pretty sure what you have above only tells the browser to not cache the page you are on. There is no way to tell the browser to clear cache on any page. This would be a problem if there was such a way. Sounds like the solution you need is to not cache any page at all, regardless of logging out or not.
Perhaps you are getting muddled with the difference between server and client cache?
If you set output cache on your aspx page, that's server-side cache, and you have a scenario where .NET can decide whether to send pre-cached content or not, and still apply ACL rules.
If you set cache requirements on the HTTP you return using Response.Cache, that's client-side caching. Once the browser obeys the cache rules you send here, the only opportunity you will have to retract your cache rules is the next time the browser requests the page. If you set the cache to expire tomorrow, that's the next chance you'll get to amend the caching. Assuming the browser is obeying you, by the way, of which there is no guarantee.
In short, dynamic pages should not attempt to set client-side caching if you want them to stay dynamic. In fact you should actively use techniques such as the ones you mentioned to suppress Caching on those pages at all times.
Client-side caching should really only be used to assist with performance and bandwidth on the static parts of your site.
I am trying to solve a similar problem myself. This is just speculation, but if i could track a user specific header in my requests I was going to try using
HttpContext.Current.Response.Cache.VaryByHeaders["login"] = true;
in the global.asax
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if (arg == "login")
{
return User().Name;
}
return base.GetVaryByCustomString(context, arg);
}
There is a way to do it. If you are caching a page, you can add a vary parameter. For Example
[OutputCache(Duration = 60, Location = System.Web.UI.OutputCacheLocation.Client, VaryByParam = "random")]
[CompressFilter]
public ActionResult Page(PageModel model)
{
...
}
In the example above, if I pass a random variable like the ticks of the current datetime object, that will prevent the cache.

How to direct to a member page from a non-member page when a user logs in?

I have two pages, NonMember.aspx and Member.aspx. If a user comes to the site, they will go to NonMember.aspx as soon as they login, I want them to immediately be redirected to Member.aspx, but instead of doing this, it is staying on NonMember.aspx. The user actually has to go click on the menu item again to get to Member.aspx.
The links are located at http://abc.com/tools/NonMember.aspx
and http://abc.com/tools/Member.aspx.
I was doing:
System.IO.Path.GetFileNameWithoutExtension(Request.Url.ToString());
but I am thinking there is a better way, especially since I have multiple default.aspx pages and this could pose a problem
Here is more detail on what exactly I am doing:
When I run the site on my local development machine, the NonMember page points to:
http://testserver/tools/NonMember.aspx.
Requet.Url.AbsolutePath points to /testserver/tools/NonMember.aspx.
Then I am doing this:
if(url == "~/tools/NonMember.aspx")
{
Response.Redirect("~/tools/Member.aspx");
}
The above is not working and I can check if url is equal to /testserver/tools/NonMember.aspx because if I deploy to liveserver, it will fail.
When using Forms Authentication for an ASP.NET application, it will automatically redirect you to the page you were viewing before you logged in. This is why you are redirected back to the NonMember.aspx page.
It would be better if you had just one member page, and perform a check in the page to see if the user is authenticated, if so, display the member content, otherwise, display the non-member content.
Then, when the user logs in and is redirected back to the page, they will see the member content.
If you are insistent on keeping the two separate pages, then in your check you simply have to see if the current user is authenticated (through the IsAuthenticated property on the User that is exposed through the page) and then redirect to your members page. If you are on the NonMember page, you don't need to check to see what the url is (unless this is MVC, which you didn't indicate).
If you have the HttpResponse object, you can use HttpResponse.Redirect
You should check your cookie or session variable to see if the user is logged in and then use a Response.Redirect to move them to the member page.
I'm not sure I get your scenario but, let's try.
If you have public pages, the login page and private pages (member pages) and once you authenticate your users you want them to browse ONLY the private part of your website, you should check early in the processing steps (AuthorizeRequest would be a good phase) if the request comming in is for a public or for the login page which you also consider public and redirect to your private page from there (like...having a "home" page for the member area of the site and always redirecting to it once you get an authenticated and properly authorized request to some public URL of your site).
EDIT: You should check for Request.FilePath
I ended up just doing the following:
if(Authenticated)
{
string path = Request.UrlReferrer.AbsolutePath;
if (path.EndsWith("/tools/NonMember.aspx"))
{
Response.Redirect("~/tools/Member.aspx");
}
}

Categories

Resources