Introduction
Because of the stateless nature of the HTTP protocol a web server considers every request as a new one. This means that, if you want to track a series of related calls, you must establish a way of identifying if the current request is a new request or is related to another request.
One way of doing that is to generate a token for the first request and then reuse the same token for related requests. The token can be anything from a number to a complex object, the only important thing is that the token is guaranteed to be unique. If that token is sent with every request, then everything works just fine, as we can say for sure to which group the current request is a part of. We can do it the other way around too: the client makes a call with a unique token and, based on that token, the communication occurs. This can be implemented when a unique username is used as a token, for instance.
However, another problem arises, with even greater importance. What if all of the requests are related to the same object? What if we need to call the method several times on the same object and the method modifies the object? We need a way of storing the object between calls so we can modify the object with each new call.
This is what we will be talking about. Using the token concept, I will describe a way of maintaining object state between sequent calls to the same object.
The idea
There are several ways of implementing this functionality. I will discus only two of them in this article. The idea is common to both ways, what differs is the implementation details.
Let�s assume that we use the approach when the client uses a username for a token and makes a request to a web service.
The idea: There is a list of all the tokens that previously made the request. When the request is made, the web service retrieves the token from the parameters of the request and compares it to the list. If the token is not found there, then this is the first request made for that token, and a new object is created for that request. The token is added to the list of tokens.
If the token is found in the list, then this is a sequent request, and the object is available. The object is then retrieved. It will contain the current data (the data modified by the last request).
Now we have the object (newly instantiated or retrieved). We can now use the object and call the methods we need.
After we are done using the object, it is time to store it away so it will be available when we need it again.
Sounds complicated? Well, it�s not. Fortunately for us, there are already implemented systems that help us with the token list. In what follows, I will discuss two systems that are available for use with no extra effort: The Application
object and� the file system.
Using the Application object of web services
Every ASP.NET web application (including web services) has a facility called the Application State. This facility, accessed via the Application
property enables the storage of different objects between requests. Objects stored in this way are available to the entire application.
A long discussion would be required about Session State and the ways to store it. Actually, you can use a State Server, or even a SQL Server to hold the State data. If you are developing large applications, this is the way to go. However, for smaller applications, these solutions are, most of the times, not necessary.
I hope it is clear that the Application
object is what we need. Objects can be stored in the Application
object like in a hash table. You could use the token (which is unique) as a key and store the object as the value.
This solution is great; however, it does have a drawback: the state is not persistent all the way. When the server is rebooted, the state is lost. Also, the ASP.NET worker process might be recycled by the Internet Information Services (IIS). This means that, sometimes, you might end up losing the data you have inside the Application
object.
Programming using this facility is extremely simple, as you can see in the code below:
public string MethodCall_ver1(int token)
{
UserDefinedClass d = null;
if (Application[token.ToString()] != null)
{
d = (UserDefinedClass)Application[token.ToString()];
}
else
{
d = new UserDefinedClass();
}
Application[token.ToString()] = d;
return string.Format("{0}", d.something.ToString());
}
We only have to check if an object is stored at the location specified by the token. If it is not, then we instantiate a new object. After we are done using it, we store it in the Application
object.
This solution is great if you have requests that are closer in time to one another and you don�t care very much about losing the data. However, when you are interested in keeping the state between requests whatever happens, and don�t what to use additional servers, the solution presented below can be used.
Using the File System
The file system is a great way of storing objects between system restarts. Also, even if the file system is arbores cent in nature, there still exists a way of uniquely identifying a file inside a folder: by using the file name. So, the token can be used as a file name.
When the request comes, the method will search for a file with the name as the token. The idea remains the same, only the implementation changes. Instead of looking in the Application
object, you check to see if the file exists. There is a catch though. We need to take an object from memory and store it on the file system. This means that we have to invoke a process known as serialization in which the bytes of the object are stored into a file stream. The reverse process is know as deserialization, and consists in reading the bytes of the object from a stream and recreating the object.
Fortunately for us, the .NET framework allows us to do just that, and very simple! The BinaryFormatter
class does just that. Below we have the code that does that. As you can see, the only thing that changed is the way we save and retrieve the object:
if (File.Exists(string.Format("{0}{1}.dat",
System.Configuration.ConfigurationManager.
AppSettings["TemporaryDirectory"], id )))
{
FileStream sr = new FileStream(string.Format("{0}{1}.dat",
System.Configuration.ConfigurationManager.
AppSettings["TemporaryDirectory"], id),
FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
d = (UserDefinedClass)bf.Deserialize(sr);
sr.Close();
}
else
{
d = new UserDefinedClass();
}
FileStream sw = new FileStream(string.Format("{0}{1}.dat",
System.Configuration.ConfigurationManager.
AppSettings["TemporaryDirectory"], id),
FileMode.OpenOrCreate);
BinaryFormatter abf = new BinaryFormatter();
abf.Serialize(sw, d);
sw.Close();
return string.Format("{0}", d.something.ToString());
The TemporaryDirectory
is stored in web.config and the entry in that file is:
<add key ="TemporaryDirectory"
value="C:\Inetpub\wwwroot\WebServicePersistent\temp\"/>
This is all that needs to be done in order for us to have a web service that persists its state between requests.
This solution has the advantage that the state is persisted even between system crashes and system reboots. This is a great thing. However, we have the problem of authorized access. The files stored on a server can be removed by people with not so good intentions. This means that, if the objects contain sensitive data, we have a breach. In order for this not to happen, we have to set the appropriate ACL (Access Control List) on the folder where we store the serialized objects and make sure that IIS does not serve them to anyone!
Testing the solution
Well, to do that, we just have to call the web service repeatedly with the same token, and see how the something property continues to increment.
Performance consideration
While storing objects in the Application
object might seem the better solution, as the number of requests that need to be served increases, all of the objects get stored in the server memory. This might be a huge problem, and could even result in a DDoS attack.
On the other hand, while using the file based approach, you incur a penalty for each read and write to the disk. However, the memory is only allocated when it is needed, so no DDoS.
Conclusion
If you want to have persistent state between sequent calls to a web service, you can employ one of the solutions I presented. Either use the Application
object for storing the objects, or serialize the objects and store them in a file, on disk.
The Application
object approach is better suited for small objects that are accessed frequently, while the file approach is better suited for big objects that are accessed not as frequently.
In any case, the solution you decide to implement is entirely up to you and the technical requirements of the application you develop.
Happy coding!