In this tutoring we would show the basics of using sessions in CppCMS --- how to store connection persistent data in stateless HTTP protocol. |
|
## Table of Contents |
|
- [Code](#cd) |
|
- [Templates and Content](#tc) |
- [Saving data](#sd) |
- [Fetching Data](#fd) |
- [Configuration --- behind the scenes](#conf) |
|
- [Backend](#back) |
- [Duration options](#dur) |
|
## <span id="cd"></span>Code |
|
Our current code would be based on [start with forms](/wikipp/en/page/tut_forms) code. |
|
### <span id="tc"></span>Templates and Content |
|
We would save the information about user using `session_interface` object that is a member of application. This information would be displayed every time it visits the page. |
|
First of all let's change our "data.h": Instead of showing person's sex and state we should display a prefix "Mr", "Miss" or "Mrs" according to our saved parameters. |
|
So lets change line: |
|
string name,state,sex; |
|
to |
|
string name,who; |
|
And we would use following template: |
|
<h1>Hello <% who %> <% name %></h1> |
<% if (content.age != -1.0) %> |
<p>Your age is <% age %></p> |
<h2>Change details</h2> |
<% else %> |
<h2>Input your details</h2> |
<% end %> |
<form method="post" action="" > |
<% form as_p info %> |
</form> |
|
__Notes:__ We can inject arbitrary C++ code into conditions between two brackets `()` (line 2). However, now we should refer to all content class variables using `content` member. Templates engine would not substitute correct prefix for you, because it is native C++ code. |
|
### <span id="sd"></span>Saving data |
|
Now let's rewrite out `hello::info` function: |
|
First of all let's load new data from the form when it submitted to session object, instead of content. This information is preserved withing different requests: |
|
data::message c; |
if(env->getRequestMethod()=="POST") { |
c.info.load(*cgi); |
if(c.info.validate()) { |
session["name"]=c.info.name.get(); |
session["sex"]=c.info.sex.get(); |
session["state"]=c.info.martial.get(); |
session.set("age",c.info.age.get()); |
c.info.clear(); |
} |
} |
|
|
#### Notes: |
|
- Note on operator `[]` of session object: |
|
string &session_interface::operator[](string const &) |
|
It's semantics similar to semantics of `std::map`'s `[]` operator. Accessing it's member returns the reference to the string by the key. If key not exists, it is created. |
|
- The storage object is std::string. In order to store other different values, you should do lexical cast to string. The simplest way is to use template member function |
|
template<typename T> |
void set(string const &ket,T const &value); |
|
|
Now, our session data would be preserved between requests and we can fetch it: |
|
### <span id="fd"></span>Fetching Data |
|
if(session.is_set("name")) { |
c.name=session["name"]; |
if(session["sex"]=="Male") { |
c.who="Mr"; |
} |
else { |
if(session["state"]=="Single") { |
c.who="Miss"; |
} |
else { |
c.who="Mrs"; |
} |
} |
c.age=session.get<double>("age"); |
} |
else { |
c.name="Visitor"; |
c.age=-1; |
} |
render("message",c); |
|
#### Notes: |
|
- First time, you ask for any field, you should check if it is exists: |
|
if(session.is_set("name")) { |
|
It is incorrect to write: |
|
if(!session["name"].empty()) { |
|
Because you would change session object and set the |
value of "name" to empty string, and this value would |
be saved. This is probably not what you want. |
|
Then, if you always set all fields together, it is safe |
to fetch them directly. CppCMS sessions guarantees |
consistency of the data. |
|
- You can fetch non-string values using template member function: |
|
template<typename T> |
T get(string const &key) |
|
- `get` and `set` template member functions use `boost::lexical_cast` to convert the data. You can't use them for types that are unsupported by it. |
|
If you want to store arbitrary C++ object you may want |
to use `cppcms::serializable` and convert it to string. |
|
|
## <span id="conf"></span>Configuration --- behind the scenes |
|
### <span id="back"></span>Storage Backend |
|
CppCMS has several options to session management. In every case cookies are used --- there is no "GET" or "POST" methods for storing session information like `/page/?sid=2e7f60c43b88d4b554a`. |
|
The developer has several options to save information: |
|
- Store all data at client side in signed and optionally encrypted cookies |
- Store all data at server size and use session id to retrieve the information each time |
- Use combination of them: If amount of data too big to be |
stored in cookies, it would be stored on server side. |
|
We would add following lines to our CppCMS configuration file: |
|
session.location = "client" |
session.cookies_key = "10b74ef35431d95c420dc67b52b3f166" |
|
#### Notes |
|
- Defining session location, would enable session interface. Most suitable option for 99% of cases is to store the data in signed and encrypted cookies on client side. |
- The default method for client side storage is encrypted and signed cookies, unless CppCMS was compiled without libgcrypt. Then, it falls backs to HMAC digital signature, that provides consistency but allows user to see the data. |
- When using client side storage, a private key of 128 bits should be provided as hexadecimal number of 32 digits length. |
|
You should keep the private key unique per application and never disclose it. You may generate such key easily using `cppcms_make_key` utility that uses [`/dev/random`](http://en.wikipedia.org/wiki/Urandom) interface. |
|
### <span id="dur"></span>Duration Options |
|
You can define several session duration options: |
|
- Browser based, until window is closed --- "browser" -- the default one. |
- Time based with renewal --- "renew" |
- Time based with hard limit after first time creation --- "fixed". It is useful when you want to force user to login every predefined period of time. |
|
Each type of expiration has its time limit (even browser one has its limit). It can be defined using `session.timeout` parameter. The default one is 24 hours. |
|
For example: |
|
session.expire = "renew" |
session.timeout = 604800 # Week |
|
|