Got a really simple question. I'm doing the railstutorial by Michael Hartl and it talks about using the session method:
Logging a user in is simple with the help of the session method defined by Rails... We can treat session as if it were a hash, and assign to it as follows:
session[:user_id] = user.id
It says you can treat session as if it were a hash, but I'm confused because it is called the session method, so is anything actually being called? My guess is that by inserting into the session hash, there is a session function that looks into the hash to see if there is anything present? I'm not really sure how it works.
Would be rude not to mention the Session documentation:
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
Basically, each time someone visits your Rails app, it will create a small cookie in their browser, identifiable by a unique ID (not user ID).
This cookie is essentially a Ruby hash, hence why you can store hashed data inside it:
session[:your_hash] = "TEST"
This will allow you to store small snippets of data (such as user_id or others) through requests.
The main reason Rails has this is down to HTTP being a stateless protocol.
Stateless protocols are contrary to stateful protocols; they don't retain the state between requests, thus you have to reinvoke data, etc, each time a new instance of the application is accessed.
Simply, this translates into Rails being a "dumb" system - only remembering data you send it each request. Session variables have been used by developers for decades to provide base information about users / preferences etc, allowing you to "rebuild" a user with each request.
This is why you have to save the user_id as a session - each time you wish to reference a logged-in user's data, it has to be built from that id stored in the sessions hash.
It is a method that returns an object which supports (some of) the same methods a Hash object supports, such as [] and []=. Actually, it is an ActionDispatch::Request::Session object.
Related
I want to know if it is possible to define a value for a session variable in a view page (.html.erb) and use it in a controller?
For example:
in order controller, new.html.erb:
session[:amount] = #order.amount
in payment controller file:
#amount = session[:amount]
I have a variable in my controller which its value should be changed based on variable I get in one of the views. As the value is stored in a session, I need to use the session value in my controller. Thank you in advance for descriptions and replies.
As I tried, I found that it's possible to pass a parameter from a view to a controller using a session variable. The problem I had was due to the type of the variable's value. I used a session variable and changed its type by using the "flood" function, and the problem is solved.
I asked the question in its general form to know more about the session variables but unfortunately I've not received proper answers.
You say
which its value should be changed based on variable I get in one of
the views
How does the view get the value ?
If you get it by user input:
Using ruby in the view won't help, since the user is interacting with a HTML page. You should use a form POST, possibly asynchronous to make a server call which will set the new value to the rails session.
If you get it by calculating something in ruby:
Then you could and should probably do it from the controller and not from the view. And yes, you can store values in the rails session.
Also, if you are not sure where your problem is (either session usage or Stripe API usage), I suggest you isolate both problems to figure out a solution.
For instance try setting any hardcoded value in the session in one controller method then reading it in another one. When that works, use it in combination with Stripe.
The problem you are facing is to have a persistence data across multiple requests.
You cannot set a session in the rails view. As the whole principle of session is to have persistent data on the server side. To tackle your problem, you can make use of cookies. Cookies are used for storing persistent data across the request on client side and are sent to the server with each server request. Setting a cookie in your view and using it in a controller will serve your purpose.
To my understanding,
when a session is initialized for some user,
the session gets a hash and session_id which identifies it.
ex. session[key]=value
session_id = 23f8fzsj2048j20j
Now, when logging out a user, I know you can simply set
session[:user_id] = nil
But what happens to the actual session hash?
If I'm not wrong, if there is User A, B, and C, they each get assigned a unique session, for example with User A with session_id = 12345abc, User B with session_id = 23456abc, and so on.
Does this not create infinite amount of session hashes then?
Are they garbage collected when they do not get used?
This depends entirely on the mechanism you're using to store hashes, but the short answer is: No, sessions are not garbage-collected, and they generally don't need to be.
Sessions are (by default) stored using CookieStore. There is no server-side data, all of the cookie data is encrypted and stored in a cookie. The user's browser is responsible for clean-up.
You can use alternative session storage engines, such as ActiveRecord sesssion store which stores session data in the database. Doing this requires you to manually choose when to consider a record "expired", and to implement your own clean-up code, again, they are not automatically "garbage collected": How does Rails know when to delete a record from the `sessions` table?
I am not sure what you mean by session_id, but by default sessions are using a cookie that is saved in your users' browser.
If you'd like more reference for the code involved I suggest you read this, https://github.com/rails/rails/blob/3ac3760c69e6e6914c5ddae138856b3c82ac0f20/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb, and then go on and see the Rack implementation for cookies.
As for nullifying a session in the context of logging a user out, most authentication frameworks check for a special cookie with a user id that is set once a user has logged in and when that value is not found in the cookie your are essentially logged out.
The hash itself which gets initialized in the request cycle lives in memory and will be garbage collected at the end of the request-response cycle, in a very naive way, your hash lives in the heap and gets garbage collected.
The data that it is referencing to lives in your browser's cookiejar and has it's own mechanisms for cleaning it.
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.
In fairly new to MVC and I would like to use a session. I have a base controller and all my other controller inherit from my base. I need the session checked every time a page is hit.
What is the best way to go about this?
Updated
My session will need to store an id to be able to build the pages correctly. If the session doesn't have the ID I need to look up the information in DB. I don't want to use cache because IDs could be different for different users.
I recommend going the cache route.
Create a class called 'CacheHelper' and within it, a method called 'GetId()'
In the GetId() method, setup a Dictionary object to store your values and use the username as the key.
Each time you call GetId, check to see if the Key exists in your dictionary
myDictionary.ContainsKey(username);
If not, look it up in the database, add it to the dictionary, then resave it to cache.
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.