We have already learned how about the verbs: PUT, DELETE, POST and GET, and we have exposure to Basic and Digest Authentication, so now we need to actually PUT or DELETE something.
PUT and DELETE in this context are used as the method on your http request. Each HTTP request has a method if you did not already realize it. If you look at the IIS logs for any IIS server you will see many POST and Get requests because those verbs are normally associated with web browsing. The SData team and based on, I am guessing, the RESTful web services guys recommendation that certain verbs be used when accessing RESTful resources over http (not your typical web browsing).
PUT is to tell SData that you are Updating and DELETE is to tell SData that you are deleting. That is just fine. So how can you actually do the telling? First we know how to do a get on any resource:
System.Net.WebClient client = new System.Net.WebClient();
client.Encoding = Encoding.UTF8;
client.Headers.Add(System.Net.HttpRequestHeader.ContentType, "application/atom+xml");
client.Credentials = new System.Net.NetworkCredential(_User, _Password);
string result = client.DownloadString("http://localhost:3333/sdata/slx/dynamic/-/Tickets");
where the url is the RESTful end point for all tickets. This will return a feed of Tickets. If you wanted just a single ticket you could:
string result = client.DownloadString("http://localhost:3333/sdata/slx/dynamic/-/Tickets('123123123')");
where TicketId is 123123123.
You can also perform searches like
string result = client.DownloadString("http://localhost:3333/sdata/slx/dynamic/-/Tickets?where=ticketid eq '123123123'");
and so on. Documentation for the Query Expression is forthcoming and remind me to update this if 7.5.2 is out and you do not yet have it (I will add a link).
The Ticket comes back in the result and has a root node of feed (it is all xml). Within the feed are various items including an entry which contains the actual ticket and the tickets payload. To retrieve that we can do something like :
(snippet of code – this is well documented in the developers subscription) :
XPathNodeIterator nav = xmlNav.Select("//slx:Ticket"manager);
nav.MoveNext();
return nav.Current.InnerXml;
Now we have an xml file (XmlDocument type) and it contains the values for the Ticket we selected. We wanted to update something. Each value will be returned as a node:
You need to get one of these nodes. Here is the code to get back the Area:
You need to know that passed in is a valid xml document (xdoc and the node you want: “//slx:Area”)
var manager = new XmlNamespaceManager(xdoc.NameTable);
XmlNamespaceManagerAddNamespacesforslx(ref manager);
XmlNode xnode = xdoc.SelectSingleNode(sNodeName, manager);
return xnode.InnerText;
But this will not update the value, it will only return it. We need to get something to update.
(this code was note written by me, but only edited):
private void UpdateSingleValue(string nodename, string nodevalue, ref XmlDocument doc)
{
var xmlNav = doc.DocumentElement.CreateNavigator();
var manager = new XmlNamespaceManager(doc.NameTable);
XmlNamespaceManagerAddNamespacesforslx(ref manager);
var nav = xmlNav.SelectSingleNode(nodename, manager);
if (nav.Value != nodevalue)
{
var nil = nav.SelectSingleNode("@xsi:nil", manager);
if (nodevalue == null)
{
if (nil == null)
{
var attributes = nav.CreateAttributes();
attributes.WriteAttributeString("xsi:nil", "true");
attributes.Close();
}
}
else
{
if (nil != null)
{
nil.DeleteSelf();
}
}
nav.SetValue(nodevalue);
}
else
{
nav.DeleteSelf();
}
}
Once we have an xml doc containing and entry and a payload node we can tell it to go back to the server and update right? Not so fast. We still need to pass in an if-match header. This ensures that the record has not been updated since we last retrieved it. How do we get the if-match header value? It is actually in the entry node in a node called etag:
We need to get the etag (like we did for the single value) and this time we use it. We take the etag and add it to the request headers as the if-match and PUT our data. Our response should be 200 and the database should be updated:
private bool SendUpdatedEntryViaPUT(XmlDocument _xmlCurrent, string url)
{
Uri uri = new Uri(url);
System.Net.HttpWebRequest request = null;
request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "PUT";
request.ContentType = "application/atom+xml;type=entry";
request.Headers.Add("if-match", GetSingleValueFromxmldoc(_xmlCurrent, "//http:etag"));
UTF8Encoding encoding = new UTF8Encoding();
byte[] postBytes = encoding.GetBytes(_xmlCurrent.InnerXml);
request.ContentLength = postBytes.Length;
System.IO.Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
request.PreAuthenticate = true;
request.Credentials = new NetworkCredential(_User, _Password);
string result = string.Empty;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8))
{
result = readStream.ReadToEnd();
//MessageBox.Show(result);
}
}
}
return true;
}
What about DELETE? That is the easy part. For DELETE we should not need the etag and change the method to DELETE.
That’s it!
