Part 2 - Set up Drupal 7 REST web server and create a .NET client to perform CRUD operations

In my previous post, I have explained how to configure Drupal 7 REST web server and demonstrated how to post a new node using Fiddler 2. It was just a proof of concept without writing any codes. In this post, I will continue where I have left off with more concrete example. I will develop a very simple ASP.NET application to post a file to Drupal 7 via RESTful service.

One of the challenges I had during the investigation was to understand how Drupal 7 REST server handles Drupal's file nodes and how the REST server associates a newly uploaded file with a specific content-type node that has a file field. To find out, I have decided to retrieve the list of file nodes by invoking the GET operation. In order to access file resource, I need to log in to the system. Previously, I had enabled the file resource under rest end point for this example (see my previous post). To access the file resource via rest end point, open a browser and type your URLs (e.g., http://localhost/rest/file.xml for XML format and http://localhost/rest/file.json for JSON format).


If you inspect each item closely, you will see fid field which identifies the unique ID and uri which indicates the URI to access the detailed information of a specific file node. In my example, to access a file node #30, I need to access http://localhost/rest/file/30.


In details view (as shown above), you can see the uri_full field which contains the full path of sample.pdf. The file node #30 is located in default site's file location (http://localhost/sites/default/files/). If you copy this path in your browser, you will be prompted to download or open this pdf document. So, if you have an article page (node) that contains a file field (fid value), you will be able to retrieve a file associated with this article by simply performing GET operation. What you need is a uri_full path of the file you wish to retrieve. I think the entire process is straight forward.

Now the question is how can I post a file using REST? Initially, I was not able to find any example or documentation from Drupal site, but I got a hint from tests folder which is located under ~/site/all/modules/services. In fact, there are a tons of useful information in various test cases to figure out how to perform a POST request using JSON format. If you wish, you can check these test files and use them as you see fit. For the purpose of this article, let me explain the whole process in my own words.

To upload a file in JSON format via REST, I need to provide 3 values: file name, file content and user id, a person (account) who is uploading this file. I am using 1 in this example (admin account).

JSON format:

{“filename”: “something.pdf”, “file”: “file content in base64string”, “uid”: “1”}

I think the tricky part is to convert the byte stream of your file into an array of base-64 string. My understanding is that since the JavaScript Object Notation (JSON) is a text-based standard, I have to format the file content in string format enclosed in double-quotes. Fortunately, .NET framework provides a conversion function which I am going to use in my example below. Just be aware that the size of the converted file content will be larger than original file. In addition, I have noticed that Drupal REST server (contribute module) does not allow uploading contents in chunks (honestly, I could not find a way to do it with current REST Server module). So, the entire contents will be uploaded and stored in  PHP memory. This is why I decided to increase memory_limit settings in php.ini configuration file. For details, please read my previous post.

Let's start writing some codes.

To convert the byte stream into an array of base-64 string, I am going to use a static function called, Convert.ToBase64String which takes byte[] as a parameter. To feed the file content in byte array, I decided to create a function that reads the file from local disk or URL and then, return the content in byte array. This is fairly straightforward so I will skip the explanation.

static byte[] file_get_byte_contents(string fileName)
{
filesize = "0";
        byte[] sContents;

if (fileName.ToLower().IndexOf("http:") > -1)
        { 
// URL 
System.Net.WebClient wc = new System.Net.WebClient();
sContents = wc.DownloadData(fileName);
        }
else
{
// Get file size
FileInfo fi = new FileInfo(fileName);

// Disk
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);

sContents = br.ReadBytes((int)fi.Length);

br.Close();
fs.Close();
}

return sContents;
}


In this example, ToBase64String function takes a byte array of sample.pdf file (which is returned by file_get_byte_contents() call) and convert it into a base-64 string.

string filenamepath = @"C:\temp\sample.pdf";
string filevalue = Convert.ToBase64String(file_get_byte_contents(filenamepath));


Then, create a POST request in JSON format.

string data = string.Format("{\"{0}\": \"{1}\", \"{2}\": \"{3}\", \"{4}\": \"{5}\"}", "filename", "sample.pdf", "file", filevalue, "uid", "0");


Now I am ready to upload the file into Drupal site.

First, create HttpWebRequest object with URI for file resource end point. In my example, I am using http://localhost/rest/file.

HttpWebRequest request = (HttpWebRequest) WebRequest.Create(@"http://localhost/rest/file");


Prior to submitting this POST action, I need to get the authenticated session information. You can find how to get the authenticated session information from my previous post (see the step #19).

request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(new Uri(@"http://localhost/rest/file"), new Cookie("SESSa0bb865e42c8jkl100000eed3d29106c", "9McU_zwIl9G4xPlYSGWtTx-6A9a1zDoCqchJH1Ey-WQ"));


Add application/json content type and POST as a method of this action.

request.ContentType = "application/json";
request.Method = "POST";
request.KeepAlive = true;


Convert the data in JSON to byte stream and get the length.

byte[] bytes = Encoding.UTF8.GetBytes(data);
request.ContentLength = bytes.Length;


Once ready, get a stream object from HttpWebRequest and write byte contents in the stream.

Stream rs = request.GetRequestStream();
rs.Write(bytes, 0, bytes.Length);


If needed, you can gracefully handle the error message by adding try block in your code. In my example, I used this block to troubleshoot the problem. In fact, it helped me to identify why a file larger than 1MB failed while performing a POST operation in my Drupal testing environment.

try
{
WebResponse response = request.GetResponse();
StreamReader responseContent = new StreamReader(response.GetResponseStream());
}
catch (Exception e)

{
WebResponse errResponse = ((WebException)e).Response;
using (StreamReader responseContent = new StreamReader(errResponse.GetResponseStream()))
{
        Console.WriteLine("error: " + responseContent.ReadToEnd());
        }
}


You can now open the browser and go to your Drupal file resource end-point (e.g., http://localhost/rest/file.xml) and check a newly uploaded file node.


Comments

Post a Comment

Popular Posts