MVC Cache Based on Variables in Session and Custom Security Logic - asp.net-mvc

I have an application that shows many charts and tables using JQuery. Some of these charts are based on variables that are saved in the session (E.g. user added a value in another page and in the next page I am generating a chart, so the user request doesn't send any parameters)
I was looking around on the net and most of the solutions are based on
[OutputCache(Duration=60, VaryByParam="someParm")]
The problem is most of my request don't send parameters, they just use values that are in the session.
Is there any way to enable cache for these kinds of requests?
Edit: We have a complex security requirement that we couldn't use the default authorization attribute of MVC. We had to write logics based on the current user + the parameters sent to the action, so a method inside the action decides either to go ahead with the request or returns nothing. This makes caching very difficult because at the time OutputCache is executed we just have parameters, but identity object in the context is empty. As a result, if a user with admin privilege send a request for a and b and after him someone with minimum privilege send request for a and b, the second person will see the result because the action didn't run, but the value from the cache is used!
To solve this problem I used the getvarybyCustome. All this function does is to return user's group name which helps to create a more complex key. The person with minimum privilege in the last example will have different cache key (a,b,group_less) than the admin's request cache key (a,b,group_admin). However, getting's group name for each request is expensive as well, so I use Cache object to cache user's group, so at the beginning of the session the user's group is queried from AD and saved to cache, so for his/her later requests, his group name is retrieved from cache.

If something you can't achieve by VaryByParam then you can try VaryByCustom. See an example here

You could make a redirect of this request and send it to a new controller method sending the session parameters, by this way in a future implementation may be you use query string parameters instead of session and your code will work too.
You could make a method for conversion of this session parameters on a base class of all your controllers, to write the conversion once.

Related

Save global attribute value when new session starts

I have two fields in SAP Fiori App: Template_ID and Offer_ID.
I want to choose value in Offer_ID depending on Template_ID field value.
For solving this problem I've tried to do this steps:
When the user click on Template_ID field in Back-End runs the method:
CL_CUAN_CAMPAIGN_DPC->contentset_get_entityset().
This method has returning paramater et_result. In et_result I have the necessary field temp_id.
For saving temp_id value I created a global attribute in class ZCL_CUAN_CLASS.
ZCL_CUAN_CLASS=>GV_CONTENT = VALUE #( et_result[ 1 ]-temp_ID OPTIONAL ).
I'll use this global attribute as an input parameter for my second method:
CL_CUAN_CAMPAIGN_DPC->GET_OFFER_BY_TEMPLATE().
This method returns to me the internal table with the offer_id, which belongs to my choosen temp_id.
But when the user click on Offer_ID field on Web UI, in debugging I see that my global attribute is blank.
May be it's because of session or something else, but it's blank.
OData is a stateless protocol, meaning the server responds your query, then forgets you were ever there. By definition, this does not allow you to transport main memory content from one request to the next.
User interfaces on the other hand usually require state. It can be gained through one of the following options:
Stateful user interface
As Haojie points out, one solution is to store the data that was selected in the user interface and submit it as a filter criterion back to the server with the next request. Having a stateful user interface is the standard solution for stateless server apps.
Stateful persistence
Another option is to store the data permanently in the server's database, in ABAP preferredly in a business object. This object has a unique identifier, probably a GUID, that you can reference in your requests to identify the process you are working on.
Draft persistence
If not all information is available in one step, such as in a multi-step wizard, should not become "active" right away, or you want to be able to switch devices while working on a multi-step process, drafts are an option. Drafts are regular business objects, with the one specialty that they remain inert until the user triggers a final activation step.
Soft state
For performance optimizations, you can have a look at SAP Gateway's soft state mode, which allows you to buffer some data to be able to respond to related requests more quickly. This is generally discouraged though, as it contradicts the stateless paradigm of OData.
Stateful protocol
In some cases, stateless protocols like OData are not the right way to go. For example, banking apps still prefer to pertain state to avoid that users remain logged in infinitely, and thus becoming vulnerable to attacks like CSRF. If this is the case for you, you should have a look at ABAP WebDynpro for your user interface. Generally, stateful server protocols are considered inferior because they bind lots of server resources for long times and thus cannot handle larger user numbers.
When ther user click on OfferId field, it will start a NEW session and of course what you store as GV_CONTENT in class ZCL_CUAN_CLASS is lost.
What you should do is that for the second request you should send to backend with filter Template_ID so in your CL_CUAN_CAMPAIGN_DPC->GET_OFFER_BY_TEMPLATE() method, you can further process the result by Template_ID.
Or SET/GET Parameter.

MVC - Store variable to next request

I have one method which receive two parameters (int?, string). Action in my controller must save this variables and return view with form. After form is returned from client I need this variables from previous request. I can pass them to view with ViewBag and add to form as hidden, but it is very dangerous, anyone can change it in browser. Any ideas?
I think this question does not make sense.
If I have method which receives two parameters with HTTP GET method, so it's no difference where user change this two variables, in get method or in form.
If I store it in server side, he can change when passing parameters to first method and server will store wrong variables.For example:http://www.someurl.com/Controller/Action/?id=123&key=someKeyI can change parameters by editing url, so absurdly store it as variable at server side to make sure that user wouldn't change it by editig hidden fields. Moreover I can check if key is the same key as in database with this id.
Session time is 20 or 30 minutes. User can open this form and submit it when the time of session is out. Users don't like exceptions
It's pretty standard to allow forms to expire after a certain amount of time. You can keep sessions open forever as that's a security hole.
As for the actual value, you can remove it from the session as soon as the second page has been processed. If the value is not there when the second page is hit, simply redirect to the first one again and show a message that says that it took too long time.
Your second option is to use TempData with is specifically designed for storing values between two pages. TempData cannot be accessed by the user.
The third option is of course to store the value in a database (to keep it as long as you want, between logins etc)
You can store any object you like in the current session of the user. In your controller you can use:
// Save value to session
Session["MyVariableName"] = 32;
// Read value from session
int myVariable = (int)Session["MyVariableName"];
For more info regarding the session see: https://msdn.microsoft.com/de-de/library/system.web.httpcontext.session(v=vs.110).aspx

Struts2 how to keep form values when navigating between pages?

I’m using struts2 + spring3 for my project. Please help me to resolve this problem.
In my app, (a.jsp) relate to --> (aAction.java).
From the main menu, user can access a.jsp. There are so many fields in the a.jsp that user need to key in data. In the middle of the page, user needs to go another page (b.jsp) to add some more details. (b.jsp page is not a popup window) After adding data to b.jsp, user needs to come back to a.jsp. How can I retain a.jsp page data?
Only one action use for both pages (aAction.java).
1) I can keep hidden variables in b.jsp and populate data again in a.jsp. But there are so many
fields in a.jsp. So this way is tedious.
Is there any way to handle this problem with bean scope? Or any other way to do?
There are several ways to do this
use redirectActions in struts.xml. When defining results you can set the type to redirectAction. Then you can redirect to another action and pass params that will be added as parameters to the url
http://struts.apache.org/release/2.1.x/docs/redirect-action-result.html
use localStorage instead of session storage. Limit is 5 MB. Care needs to be taken when using session storage as it can affect the server's performance
if you are using struts, you can make your own type converter for passing any object. Alternatively you could create an string array of the values you want to persist and pass it as a param in struts.xml (see above). Struts has built in type converter for persisting string arrays between pages/actions
you could also save them in cookies and then delete the cookies as soon as the values are not needed as there is a limit on the number of cookies browsers can support
I usually use session storage as a last resort for the reason mentioned above

How do I maintain a specific query string across all requests?

If someone goes to our website with a query string key of ref (for example mysite.com/AboutUs?ref=Test), is it possible to maintain that query string on all future links/form posts?
So for example, I may have a link on our website to go to mysite.com/Products. I would want this to be translated into mysite.com/Products?ref=Test.
Ideally I want to know if this is possible to do by inspecting the previous URL and adding it to the current URL when the request for the page is made, maybe by intercepting the route and appending the key (if it doesn't exist already).
The project is an MVC 4 application.
You could actually pass it along by adding it to every single URL, but that would require manually adding to each use of Html.ActionLink, etc. (or I suppose you could create a custom HTML Helper to do that for you, but then every developer who works on your project would need to remember to always use the custom helper), as well as all redirects and such in your controller actions. Long and short, this would be very time consuming and very fragile.
Your best bet is to simply intercept the initial request with the querystring parameter and then set a session var. Then, you can simply check for the presence of the session var instead of the querystring parameter.
To handle all this logic, your best bet is to create a global action filter. There's pretty extensive documentation on Filters at MSDN. Once you create your filter, you just have to register it in FilterConfig.cs (in App_Start) to make it global.
Set the URL parameter in a cookie, and later in your code do whatever you want to do based on presence of that value in either the cookie or URL.
if(parameter set in cookie OR URL)
// do stuff
if(parameter set in URL)
// set the cookie so that future actions are also tagged with that parameter
Alternatively if you want such tagging to happen only for the session, set a session variable.
To do it in the way you suggested - You could rewrite all links on your page based on this tag, but that would be a roundabout, and costly, way of doing this. Most languages allow a hook that allows your code to process the HTML output before it is sent out to browser; just run a appropriate regex search and replace to get what you want. e.g. PHP has ob buffering and associated functions. You could use the equivalent for .Net.

Redirect After Post in ASP.NET MVC

I am using the Redirect After Post pattern in my ASP.NET MVC application. I have
the following scenario:
User goes to /controller/index where he is asked to fill a form.
Form values are POSTed to /controller/calculate.
The Calculate action performs calculation based on input and instantiates a complex object containing the results of the operation. This object is stored in TempData and user is redirected to /controller/result.
/controller/result retrieves the result from TempData and renders them to the user.
The problem with this approach is that if the user hits F5 while viewing the results in /controller/result the page can no longer be rendered as TempData has been expired and the result object is no longer available.
This behavior is not desired by the users. One possible solution would be instead of redirecting after the POST, just rendering the results view. Now if the user hits F5 he gets a browser dialog asking if he wants to repost the form. This also was not desired.
One possible solution I thought of was to serialize the result object and passing it in the URL before redirecting but AFAIK there are some limitations in the length of a GET request and if the object gets pretty big I might hit this limitation (especially if base64 encoded).
Another possibility would be to use the Session object instead of TempData to persist the results. But before implementing this solution I would like to know if there's a better way of doing it.
UPDATE:
Further investigating the issue I found out that if I re-put the result object in TempData inside the /controller/result action it actually works:
public ActionResult Result()
{
var result = TempData["result"];
TempData["result"] = result;
return View(result);
}
But this feels kind of dirty. Could there be any side effects with this approach (such as switching to out-of-process session providers as currently I use InProc)?
Store it in the Session with some unique key and pass the key as part of the url. Then as long as the session is alive they can use the back/forward button to their heart's content and still have the URL respond properly. Alternatively, you could use the ASP cache, but I'd normally reserve that for objects that are shared among users. Of course, if you used the parameters to the calculation as the key and you found the result in the cache, you could simply re-use it.
I think redirect after post makes much more sense when the resulting Url is meaningfull.
In your case it would mean that all data required for the calculation is in the Url of /controller/result.
/controller/calculate would not do the calculation but /controller/result.
If you can get this done thinks get pretty easy: You hash the values required for the calculation and use it as the key for the cache. If the user refreshes he only hits the cache.
If you cant have a meaningfull url you could post to /controller/index. If the user hits F5 calculation would start again, but a cache with the hash as key would help again.
TempData is generally considered useful for passing messages back to the user not for storing working entities (a user refresh will nuke the contents of TempData).
I don't know of more appropriate place than the session to store this kind of information. I think the general idea is keep session as small as possible though. Personally I usually write some wrappers to add and remove specific objects to session. Cleaning them up manually where possible.
Alternatively you can store in a database in which you purge stale items on a regular basis.
I might adopt a similar idea to a lot of banks on their online banking sites by using one-time keys to verify all POSTs. You can integrate it into a html helper for forms and into your service layer (for example) for verification.
Let's say that you only want to post any instance of a form once. Add a guid to the form. If the form does not post back and the data is committed then you want to invalidate the guid and redirect to the GET action. If say the form was not valid, when the page posts back you need a new (valid) guid there in the form waiting for the next post attempt.
GUIDs are generated as required and added to a table in your DB. As they are invalidated (by POSTS, whether successful or not) they are flagged in the table. You may want to trim the table at 100 rows.. or 1000, depending on how heavy your app will be and how many rendered but not yet posted forms you may have at any one time.
I haven't really fine tuned this design but i think it might work. It wont be as smelly as the TempData and you can still adhere to the PRG pattern.
Remember, with PRG you dont want to send the new data to the GET action in a temp variable of some sort. You want to query it back from the data store, where it's now committed to.
As Michael stated, TempData has a single purpose -> store an object for one trip only and only one trip. As I understand it, TempData is essentially using the same Session object as you might use but it will automatically remove the object from the session on the next trip.
Stick with Session imho rather than push back in to TempData.

Resources