Trapping XML Errors Using SharePoint Web Services
At the DFW SharePoint Community meeting this week we had a lively discussion about SharePoint web services and how difficult they are to debug. This actually has nothing to do with SharePoint per se – all methods that return nothing but strings are hard to debug – but it is a common pain point so I promised to post a code sample of how I deal with it.
A common scenario when working with web services is to pass in a <Method> batch to perform some operation, such as updating items in a list. If all goes well, SharePoint will respond to the invocation of the UpdateListItems method with a block of XML that contains a status code of 0x00000000, meaning that the operation was successful. If it fails, it will return an error status code instead of all zeroes and (sometimes) a description of the error. The problem, of course, is that both return values look the same from a code perspective; no exception is thrown and your method continues on its merry way as if all is good and right in the world. No Try…Catch block will ever trap this kind of error. So what do you do?
The answer is to write a quick routine to parse the return XML values and look for any node that has a return value other than 0x00000000 and either send back your own error text (if the method returns a string, for example), throw a system exception that will trip the Try…Catch block, or both. Keep in mind that each operation that you perform will have a corresponding XML node with a success/fail value; you have to loop through all the return nodes in order to insure that each operation was successful. The following code illustrates building the method for the UpdateListItems operation, passing in the required values, and parsing the return XML to determine if any errors are present. In this example I’m showing how to use both a return value and throw an exception; use either or both according to your needs.
try {
// Create a new XML document to contain our operation nodes XmlDocument xmlUpdate = new XmlDocument();
// Create the required nodes and insert the Update XML XmlNode ndBatch = xmlUpdate.CreateNode(XmlNodeType.Element, "Batch", ""); ndBatch.InnerXml = "<Method ID=’1′ Cmd=’" + sMode + "’>" + sUpdateFields + "</Method>";
// Instruct SharePoint not to break after the first error is encountered so we can get back a complete list of failed operations XmlAttribute xmlAttribErr = (XmlAttribute)xmlUpdate.CreateNode(XmlNodeType.Attribute, "OnError", ""); xmlAttribErr.Value = "Continue"; ndBatch.Attributes.Append(xmlAttribErr);
// Pass our XML to the UpdateListItems method along with the target List name XmlNode xReturn = listsvc.UpdateListItems(sListName, ndBatch);
// Instantiate a new XML document object to hold the return value(s) XmlDocument xmlResult = new XmlDocument(); xmlResult.LoadXml(xReturn.OuterXml);
// SharePoint XML always uses a fixed namespace; you’ll need your own NamespaceManager object to parse the return values XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlResult.NameTable); nsMgr.AddNamespace("sp", xReturn.NamespaceURI); XmlNode ndRoot = xmlResult.SelectSingleNode("sp:Results", nsMgr); // Find the ErrorCode node, which exists for all operations regardless of status. XmlNodeList nlResults = ndRoot.SelectNodes("//sp:Result/sp:ErrorCode", nsMgr);
// Loop through the node collection and find each ErrorCode entry foreach (XmlNode ndResult in nlResults) { // Check the value of the node to determine its status if (ndResult.InnerText != "0x00000000") { XmlNode ndError = ndResult.NextSibling; string sError = ndError.InnerText; // Set the value of string variable to hold the error code sStatus = "Update operation failed for " + sListName + ": " + sError + "."; // If you want to trip the Try…Catch, throw and exception of whatever type suits you throw new System.Exception(); } else { sStatus = "Success"; return sStatus; } } } catch (Exception e) { e.Message = sStatus; return sStatus; } |
The final result is pretty self-explanatory; either an exception that you can trap while debugging or a return string that you can parse (I know, I know…returning a string to circumvent a method that returns a string is counter-intuitive and generally a bad coding practice – that’s why I added the exception code). This works for most SharePoint web services that return ErrorCode values (which some do not) so Your Mileage May Vary depending upon what you are trying to achieve.
Eric ~
Thanks for the post! I’ve posted a follow-up over at my blog: http://solutionizing.net/2008/06/21/web-service-results-xpath-is-your-friend/
Let me know what you think!
Cheers ~
Keith
Thanks for the post. It was very helpful.
Rick McQ