Archive

Posts Tagged ‘HTTP’

Compress/Decompress a String in C#

December 26, 2012 Comments off

The feature I was working on today involved saving some temporary data into an HTTP session cookie. The data was an object serialized into JSON. Writing and reading cookies is easy enough but, after testing my implementation a bit, I found out that my code was intermittently failing to save the cookie in my browser. After some deeper investigation, I found that the reason my cookie wasn’t being saved (sometimes) was because the JSON I was attempting to store in the cookie was exceeding the 4K limit of a cookie. The object I was serializing to JSON was a List of other classes so I could not prevent users from exceeding the limit without imposing arbitrary restrictions on the usage of the feature. I also wanted to keep the data on the client’s machine, as opposed to a DB or other storage medium, because the data really did seem to belong in the user’s session. The only option I could think of to solve my problem was to compress the JSON string.

Unfortunately, when I Google’d “C# compress string” (or something like that), the results weren’t very helpful. Most of the code I came across was either for .NET 2.0 or was doing File IO and byte array manipulation with pointers. Many examples didn’t even make proper usage of using() blocks to properly close Streams. What I needed was .NET 4.0 code to Compress and Decompress strings to strings, preferably without needing to do any byte pointer magic. Fortunately, I eventually came across this some-what helpful Stack Overflow Q&A which was close enough to what I was trying to do. The OP was looking for a .NET 2.0 solution and his compressed medium was a byte[] instead of a string but, with a few simple modifications, I was able to adapt the code to do exactly what I needed.

using System;
using System.IO;
using System.IO.Compression;
using System.Text;

...

public static string Compress(string s)
{
    var bytes = Encoding.Unicode.GetBytes(s);
    using (var msi = new MemoryStream(bytes))
    using (var mso = new MemoryStream())
    {
        using (var gs = new GZipStream(mso, CompressionMode.Compress))
        {
            msi.CopyTo(gs);
        }
        return Convert.ToBase64String(mso.ToArray());
    }
}

public static string Decompress(string s)
{
    var bytes = Convert.FromBase64String(s);
    using (var msi = new MemoryStream(bytes))
    using (var mso = new MemoryStream())
    {
        using (var gs = new GZipStream(msi, CompressionMode.Decompress))
        {
            gs.CopyTo(mso);
        }
        return Encoding.Unicode.GetString(mso.ToArray());
    }
}

It is simple, elegant, and best-of-all… it works! Luckily for me, JSON compresses extremely well. The previous data which was failing to save was about 4.5 K but I was able to reduce that down to only 1.5 K after compression! The only drawback I have noticed so far of storing compressed data in a cookie is that it is not human readable. You could spin that as a good thing because users will be less likely to want to see/modify obfuscated cookie values, but as a web-developer, I usually like knowing exactly what is being stored on my computer by the websites I visit. An (unintentional) positive side-effect of compressing the cookie is that it helps minimize the amount of data being sent back-and-forth from the users and our servers. 4K might not seem like a lot of data, but when it is being sent in every request from the user’s browser, it can add up. Either way, this simple code helped me solve this problem… moving on!

Should I use PUT or POST for a CREATE operation in REST?

March 16, 2009 1 comment

POST is for CREATE, and PUT is for UPDATE and CREATE. Ok, they both allow CREATE, how should we choose to use PUT or POST for a CREATE? Let’s say we have an animal adoption service. We have /animals to give us a list of all the animals for adoption. Let’s say we allow the user to create a category to group the animals together, such as /animals/dogs/ to list all the dogs for adoptions. Meanwhile, /animals/dogs/{id_of_a_dog} will give us the details of a dog for adoption. Now, we want to create a new category “cats”, which will give us /animals/cats. We want to also be able to add a cat for adoption. Should it be PUT or POST?

The Method Definitions of HTTP says that “The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line.” And “The PUT method requests that the enclosed entity be stored under the supplied Request-URI.” The document also mentioned an important point “Methods can also have the property of ‘idempotence’ in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request. The methods GET, HEAD, PUT and DELETE share this property.”

In other words (plain English), we need to ask the following question:

  1. Does the user know the URI? Do we allow them to derive and decide the URI for the object to be created?
  2. What is the effect of the call to be executed twice? Is there no duplicated object created?

If the answer to both questions are true, then we should use PUT to create the categories, which means that we will PUT the request to the desired URI (animals/cats).

If the answer to both questions are false, then we should use POST to create (in this case the cat for adoption), which means that we will POST the request to the parent URI (animals/cats), in which case the response shall give us a new URL (e.g. http://somehost/animal/cats/{id_of_the_cat} ) to identify the cat that we have just created. Executing the POST again with the same request will end up creating an entry for the second cat to be adopted.

Of course, rules can be bent under certain circumstances, but we should remember “the principle of Least Astonishment” as mentioned by Scott Meyers in his  “Better Software – no matter what” talk given at SDWest, which is to strive to maximize the likelihood that user expectations about an interface are correct.

By: Nyik San Ting