SPServices Error Handling

Aug 25, 2012 at 10:51 AM
Edited Aug 25, 2012 at 10:58 AM

Hi,

I'm new to SPServices, and I was wondering if it is possible to define some kind of out-of-the-box error handling procedure, like this:

$().SPServices ({
    operation: "DoSomething",
    [option1: value1],
    [option2: value2],
    // ...
    success: function (xData) {
        alert ("The operation has been completed!");
        // some processing code ...
    },
    fail: function (jqXHR) {
        alert ("Something went wrong!");
        // some fallback code ....
    }
});

The only thing I am aware of is that you can do something like this:

completefunc: function (xData, Status) {
    if (Status == "Success") {
        alert ("The operation has been completed!");
    } else {
        alert ("Something went wrong!");
    }
}
However, this solution will also execute the success-branch when the server returns an XML-document containing error messages. Above that, I don't think it's that elegant because you would have to check the Status == "Success" every time you define a new request, which looks a littlebit like overkill.

Any suggestions?

By the way: really love this project!

Aug 27, 2012 at 10:21 AM

Oh and if this isn't possible, can I make it feature request? ;)

Coordinator
Sep 7, 2012 at 10:06 PM

Oh, I'd love to add something like you show above. There are two main layers where an error can occur:

  1. In the AJAX call itself. This really only occurs if the endpoint (SharePoint) isn't responding, usually due to the server being down.
  2. In the Web Service itself. Unfortunately there is almost zero consistency in the implementations. No two operations respond in quite the same way, and there nothing in common across the Web Services. The only commonality is that they return XML.

In all of my use of SPServices, I've found a few truths.

  1. SharePoint always responds if it's up and running.
  2. The only time a Web Service call fails is if you pass it bad data or poorly formed XML.

Early on I decided that my job in SPServices is to reduce the likelihood of 2. By eliminating the need to construct the SOAP envelopes, SOAP headers, etc., a huge number of errors simply never happen. It's up to you to test the XML coming back to see if it contains what you expect.

While I could add error handling for some of the core operations, I would have to assume quite a few things about what you are doing. For instance, you might be calling GetListItems specifically to see if you get zero items back, and that might be a success to you.

What I *have* done is to add the debug mode to many of the "value-added" functions. Because I know what ought to be coming back when I make calls the the various Web Services, I can reasonably provide some error messages that are useful for you to use in setting things up. Things like "This column <columnname> is not in the page."

I think I just wrote myself a blog post!

M.

 

 

 

Sep 8, 2012 at 12:45 PM

@samvv

Early on in my usage of the SP webservices I found the same need and came up with a "generic" way for me to detect errors in the SP response. I say generic in quotes because it was generic for my usage of those services (it meet my needs for the operations I used), but it could have shortfalls for others (ex. these methods do not currently handle an array of error that could be returned, as could be the case when batch updating/creating items on a list).  Maybe you find these useful.

Custom jQuery Methods:

/**
 * FUNCTION: jQuery.fn.hasSPError()
 * 
 *  Given an XML message as returned by the Sharepoint WebServices API,
 *  this method will check if it contains an error and return a boolean
 *  indicating that. 
 * 
 *  @alias $().hasSPError()
 * 
 * PARAMS:
 * 
 * -	none
 * 
 * RETURN:
 * 
 * 	@return {Boolean} true|false
 * 
 * EXAMPLE:
 * 
 * 	$(xData.responseXML).hasSPError();
 * 
 */
jQuery.fn.hasSPError = function() {
	var spErrCode = $(this).find("[nodeName='ErrorCode']:first");
	if (	spErrCode.size()
		&&	spErrCode.text() !== "0x00000000"
	) {
		return true;
	} else {
		spErrCode = $(this).find("[nodeName='faultcode']");
		if (spErrCode.size()) {
			return true;
		}	   
	}
	return false;
};/* jQuery.fn.hasSPError() */


/**
 * FUNCTION: jQuery.fn.getSPError()
 * 
 * 	Given a sharepoint webservices response, this method will
 * 	look to see if it contains an error and return that error
 * 	formated as a string.
 * 
 *  @alias $().getSPError()
 * 
 * PARAMS:
 * 
 * 	-	none.
 * 
 * RETURN:
 * 
 * @return {String} errorMessage
 * 
 * EXAMPLE:
 * 
 * 	alert($(xData.responseXML).getSPError());
 * 
 */
jQuery.fn.getSPError = function(){
	var error = "ERROR: Call to Sharepoint Web Services failed.";
	
	if ($(this).find("[nodeName='ErrorCode']").size()) {
		error += "\n" + $(this).find("[nodeName='ErrorCode']:first").text()
			+	": " + $(this).find("[nodeName='ErrorText']").text();
		
	} else if ($(this).find("[nodeName='faultcode']").size()) {
		error += $(this).find("[nodeName='faultstring']").text()
			+ "\n" + $(this).find("[nodeName='errorstring']").text();
		
	} else {
		error = "";
	}
	
	return error;
}/* jQuery.fn.getSPError() */

 

Example:

 I use these two methods like this:

$().SPServices ({
    operation: "DoSomething",
    [option1: value1],
    [option2: value2],
    completefunc: function (xData, Status) {
        if (Status == "Error") {
            alert ("Unable to communicate with Sharepoint Server!");
            
        } else if ($(xData).hasSPError()) {
            alert("Something went wrong! Sharepoint Error: " + $(xData).getSPError());
            return;
        }
        
        // If this point is reached, everything was successful!
        alert("All was successful!");
        
    }
});

 

 

 

 

Sep 8, 2012 at 11:43 PM
Edited Sep 8, 2012 at 11:44 PM

@ptavares

That's exactly the kind of thing I was looking for! I'm only getting started with SharePoint so I really didn't have a clue of the types of error messages/codes that can be returned. Thanks!

@sympmarc

Glad I could give you some ideas ;) Didn't knew there was a "debug" mode though, will certainly take a look at it if I have some time.

 

Now I'm just brainstorming ... what if you would be able to specify the result you are expecting when you make the request? That way the library could perform some of the work for you because it knows what you are expecting it to return. If you then combine it with ptavares' method then I think it should be possible to do something like:

$().SPServices ({
    operation: "DoSomething",
    [option1: value1],
    [option2: value2],
    // ...
    allowZeroItems: false, // defaults to true
    // ...
    success: function (xData) {
        alert ("The operation " + this.operation + " to " + this.url + " seems just fine! The data is: " + xData.responseText);
        // or anything else you might want to do ...
    },
    fail: function (SPSException) {
        alert ("Could not perform " + this.operation + " to " + this.webURL + " with options + " + this.options.join (",") + ": " + SPSException.message + " (error " + SPSException.code + ")");
        // or some other kind of error handling ...
    }
});

 

You could add some custom error codes/error messages for when the server returns no error document, when the specification of the request is invalid, or when the "no permissions" page shows up (at least in 2003). This solution should also be backwards-compatible if the standard completefunc remains the same. Heck, you could even add custom methods like SPSException.logToConsole ();

I'll see tomorrow if it's possible to create a small plugin that provides this kind of functionality. It shouldn't be too difficult using the code you provided.

Thanks for the help!

Sep 25, 2013 at 7:13 PM
Edited Sep 27, 2013 at 9:29 PM
Thanks @ptavares I used your code as a starting point for my own error handling.

Before I share my "generic" SPServices error handling I've come up with that works for me, I'd like to remind everyone of why this isn't already built into SPServices. See Marc Anderson's blog post Generic Error Handling in SPServices

That said, here the code I'm using:
// PURPOSE:
//  Examines SPServices xData.responseXML for any errors. At a minimum, it will use this SP.UI framework to display
//  an error summary in the status bar which can include the name of your function and the SPServices operation for 
//  troubleshooting in the field and debugging during development. Optionally, it dumps the verbose output from 
//  $().SPServices.SPDebugXMLHttpResult() to a supplied container
//
// NOTE:
//  It is possible for the "Status" parameter in the completefunc() callback to state "success" and for
//  xData.statusText to hold "OK" when in fact there are errors. A "Status" of "success" simply means the web 
//  service call succeeded. It does NOT mean the web service did not return its own error code and message.
//
// USAGE:
//  $(xData).OutputSPError();
//
jQuery.fn.OutputSPError = function(options) {

    var opt = $.extend({}, {
        functionName:   "",             // Name of your javascript function that invoked the SPServices call
        operationName:  "",             // Name of the SPServices operation, ie: "UpdateListItems"
        msgContainer:   "#SPServices",  // [OPTIONAL] jQuery selector for HTML container to hold error messages output
        }, options);
    
    // Ensure we were chained to the right object
    if( typeof($(this).prop("responseXML")) === "undefined" )
        return;

    var responseXML = $(this).prop("responseXML");
    if( !$(responseXML).hasSPError() )
        return;
    
    var statusText  = $(this).prop("statusText");
    var message = "";

    if( opt.functionName != "" || opt.operationName != "" )
        message = "[" + opt.functionName + "] " + opt.operationName + " :: ";

    // Display the error summary in the status bar
    var strStatusID = 
        SP.UI.Status.addStatus(
            "<img src='/_Layouts/Images/error16by16.gif' align='absmiddle'>&nbsp;" + 
            message + (statusText != "OK" ? statusText + ":&nbsp;" : "" ) + $(responseXML).getSPErrorText());
    SP.UI.Status.setStatusPriColor(strStatusID, "yellow");

    // Dump the error details to the caller's message container and unhide it
    if( $(opt.msgContainer).length != 0 ) {
        var errorDetails    = $().SPServices.SPDebugXMLHttpResult({node: responseXML});
        var divStatus       = jQuery("<h1 class='_wsStatusText'></h1>").appendTo(opt.msgContainer);
        var divError        = jQuery("<div class='_wsError'></div>").appendTo(opt.msgContainer);
        $(divStatus).text((statusText != "OK" ? statusText : $(responseXML).getSPErrorCode()));
        $(divError).append("<b>" + message + "</b><br>" + errorDetails);
        $(opt.msgContainer).show();
    }
    return;
}


// PURPOSE:
//  Given an XML message as returned by the Sharepoint web services API, this method checks for an error
//
// RETURNS:
//  {Boolean} true|false
//
// USAGE:
//  $(xData).hasSPError();
//  $(xData.responseXML).hasSPError();
//
jQuery.fn.hasSPError    = function() {

    // Sometimes a web service will be reachable and the web method will still
    // return an error code XML node. So we need to check its contents
    return( $(this).getSPErrorCode() != "0x00000000");
};/* jQuery.fn.hasSPError() */



// PURPOSE:
//  Given an XML message as returned by the Sharepoint web services API, this method
//  checks if it contains an error and returnS the error code.
//
// NOTE:
//  Sometimes a web service will be reachable and the web method call will succeed with no errors
//  but it may still return an error code XML node. If so, the node value needs to be checked.
//  EXAMPLE: UpdateListItems returns "0x00000000" for success
//
// RETURNS:
//  {String} hexidecimal error code
//
// USAGE:
//  $(xData).hasSPError();
//  $(xData.responseXML).hasSPError();
//
jQuery.fn.getSPErrorCode    = function() {

    if( typeof($(this).prop("status")) !== "undefined" && $(this).prop("status") != 200 )
        return $(this).prop("status").toString();
    
    var responseXML = $(this).prop("responseXML");
    if( typeof($(this).prop("responseXML")) === "undefined" )
        responseXML = $(this);

    var spErrCode = $(responseXML).find("ErrorCode:first");
    if( spErrCode.length )
        return spErrCode.text();
    
    spErrCode = $(responseXML).find("errorcode:first");
    if( spErrCode.length )
        return spErrCode.text();
    
    spErrCode = $(responseXML).find("faultcode");
    if( spErrCode.length )
        return spErrCode.text();
    
    return "0x00000000";
};/* jQuery.fn.hasSPError() */



// PURPOSE:
// Given a sharepoint webservices response, this method will look to see if it 
// contains an error and return that error formated as a string.
//
// RETURNS:
//  {String} error message text
//
// USAGE:
//  alert($(xData).getSPError());
//  alert($(xData.responseXML).getSPError());
//
jQuery.fn.getSPErrorText    = function(){

    var responseXML = $(this).prop("responseXML");
    if( typeof($(this).prop("responseXML")) === "undefined" )
        responseXML = $(this);

    var errorText   = "Call to Sharepoint web services failed. ";
    
    if( $(responseXML).find("ErrorCode:first").text().length ) {
        errorText += "\n" + $(responseXML).find("ErrorCode:first").text()
            +   ": " + $(responseXML).find("ErrorText").text();
    } else if( $(responseXML).find("faultcode").length ) {
        errorText += $(responseXML).find("faultstring").text()
            + "\n" + $(responseXML).find("errorstring").text();
    } else {
        errorText = "";
    }
    return errorText;
}/* jQuery.fn.getSPError() */
And here is an example of how you might use it:
var spsReturn   = $().SPServices({
    operation: "UpdateListItems",
    listName: $().SPServices.SPListNameFromUrl(),
    ID: myListItemID,
    valuepairs: [["ContentType", "Journal Entry"]], // Upon submission, all journal entries use the same content type
    completefunc: function(xData, Status) {
        if( $(xData).hasSPError() ) {
            // FAILURE: Either we couldn't reach the web service or the web method DID return an error
            $(xData).OutputSPError({functionName:   "PreSaveAction",
                        operationName:  "UpdateListItems",
                        msgContainer:   "#SPServices"});
        } else {
            // SUCCESS: Able to reach the web service and the web method did NOT return an error
        }
    }
});

// We can check for the success or failure of the SPServices() call outside the completefunc()
if( $(spsReturn).hasSPError() ) {
    // Business logic for when the web service/method fails
}
Output from failure to reach the web service:
Image

Output from the web method UpdateListItems returning an error:
Image
Sep 25, 2013 at 8:58 PM
@ChrisNorton
This is nice... and a good use of SP's (2010, 2013) SP.UI.Status which I had not seen before...
Thanks for posting back and sharing your solution.. I just bookmarked this page for future reference.

Paul.
Coordinator
Oct 15, 2014 at 1:05 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Oct 15, 2014 at 1:11 PM
Marc,
I might have updated version of the two methods I posted above... Look here:

SPGetMsgError(): https://github.com/purtuga/SPWidgets/blob/master/src/jquery.SPWidgets.js#L75
and
doesMsgHaveError(): https://github.com/purtuga/SPWidgets/blob/master/src/jquery.SPAPI.DoesMsgHaveError.js#L41 (this one looks to be the same.
Coordinator
Oct 15, 2014 at 1:30 PM
Thanks, Paul. I may add these functions into the next release, if that's OK with you.

M.
Oct 15, 2014 at 4:20 PM
No problem at all. If it beneficial to you and others, feel free to do so.