2009-08-17

Providing meaningful errors in dojo.xhr calls

I've been working with Dojo lately, and one problem is the useless error messages one gets back from the server, especially when using dojo.xhr. The problem is that the response body from the server is generally some large HTML message that may not be useful for display purposes in an AJAX-based calling environment.

To remedy this, I made some modifications to the server-side (implemented in Java servlets) to capture exceptions and return a JSON structure if the request is an AJAX request. This makes it easier for the Javascript code to process error text more easily.

On the client-side, I have some basic utility functions to facilitate capturing/reporting errors from dojo.xhr calls.

First, the server-side:

Before I Start:

Before providing detail, I should note that I've created a base servlet
class that all my servlets sub-class. This allows me to centralize
operations and services for all my servlets. In the context of this post,
this becomes useful for the purposes of exception handling. The base
class checks all exceptions from sub-classes allowing centralization
of error handling based upon the type of exception that occurred.

I also have a custom request object to encapsulate the HttpServletRequest
and HttpServletResponse objects. This encapsulation allows me to add
support for additional capabilities beyond the standard servlet API
related to request/response. For example, I provide file-upload support
and still provide a single consistent interface for servlets to access
request parameters.

Some of the code provided here may contain references to custom classes I
use, but I believe they are self-explanatory.

How to detect an AJAX request:

public boolean isAjaxRequest() {
String reqwith = req.getHeader("x-requested-with");
if (StringUtil.isBlank(reqwith)) return false;
if (reqwith.toLowerCase().equals("xmlhttprequest")) return true;
return false;
}

The above works for most cases, and wrt Dojo, definitely works
since Dojo sets the proper request headers.

Capturing Servlet Exceptions:

In my base servlet class, I catch all exceptions:


} catch (Exception e) {
if (r.isAjaxRequest()) {
log.error("Exception caught for AJAX request: "+e, e);
sendJSONError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
e.getMessage());
} else {
if (e instanceof ServletException) {
throw (ServletException)e;
}
throw new ServletException(e.getMessage(), e);
}
}

I check if the request in an AJAX request, and if so, utilize the sendJSONError() method to do what I want. sendJSONError() looks like the following:

private void sendJSONError(
HttpServletResponse resp,
int status,
String message
) throws IOException {
Writer w = resp.getWriter();
resp.setStatus(status);
resp.setContentType("application/json");
w.write("{");
w.write("\"status\":");
w.write(Integer.toString(status));
w.write(",");
w.write("\"message\":");
w.write(JSONObject.quote(message));
w.write("}");
}


Capturing/display Errors in Dojo:

On the client-side, I have two utility functions. One is a generic error handling function for dojo.xhr that will popup an alert displaying the error message received from the server. The other is a function to extract the error message from the servlet response, mainly for use by custom error functions that need to do more than display an alert dialog:


mylib.xhrErrorAlert = function(
/*Object*/response,
/*Object*/ioArgs
)
{
var prefix = "Error: ";
if (ioArgs.args.myAlertPrefix) {
prefix = ioArgs.args.myAlertPrefix;
}
alert(prefix + mylib.getXhrResponseMessage(response));
}


mylib.getXhrResponseMessage = function(
/*Object*/response
)
{
var msg = response.message;
if (response.responseText != undefined &&
response.responseText.charAt(0) == '{') {
try {
var o = dojo.fromJson(response.responseText);
if (o.message != undefined) {
msg = o.message;
}
} catch (err) {
}
}
return msg;
}

The following is an example of how to use the alert function:

dojo.xhrPost({
url: url,
handleAs: 'json',
load: function(response, ioArgs) {
...
},
myAlertPrefix: 'Error doing something: ',
error: mylib.xhrErrorAlert
});

Note, there is a reliance that the message text is meaningful in the exception thrown on the server-side. Regardless, this provides a better error message than what is normally available in response.message, which normally contains the canned message the server defines for the given HTTP response code versus the message text from the underlying exception.

2009-08-16

Getting fixed-width font for Gmail messages

Gmail labs has a feature to display a message in a fixed-width font, but you have to manual select it from a menu each time you want.

The following is a quick CSS userContent.css hack for Firefox that will cause message content to always display in a fixed-width font:

@-moz-document domain(google.com) {
.ii {
font-family: monospace;
font-size: 75%;
}
}

I use "monospace" since it will use what I have specified in my font preferences in FF. The key item is the ".ii" class.

The only negative side-effect is that non-text messages, like HTML, will have the default font be monospace. However, I'm currently willing to live with that limitation.

I posted a suggestion on the Gmail labs group for Google to define CSS classes based upon content media-types, so CSS settings can be constrained for a given media-type. Who knows if they will ever do such a thing.

Disabling ads in Gmail

For those that do not know, Firefox supports the ability for a user to define their own custom CSS settings, allow the user to customize how pages appear in FF. The file is called userContent.css and is located under the chrome/ directory of your profile.

As an exercise, I tried to see if it is possible to suppress the Google ads displayed in the Gmail web interface. Well, with a little help from Firebug to examine the HTML/CSS structure of Gmail pages, I came up with the following:

.vb {
display: none;
}
.u5 {
display: none;
}
table[class~="Bs"] > tr > td:nth-of-type(+3) > div:nth-of-type(+2) {
width: auto !important;
}
table[class~="Bs"] > tr > td:nth-of-type(+3) > div:nth-of-type(+2) >
div > div:last-child
{
display: none;
}

The above works best in FF 3.5, especially since the last items utilize CSS3 selectors.

FF supports the ability to limit the settings for a given domain. I Include the above in the following block so it only applies to google.com sites:

@-moz-document domain(google.com) {
...
}