Handling Web Service exception in Flex code

In this post I’ll explain how to propogate exception details from Web Services into Flex. I am using java XFire web service implementation but similar steps can be applied to different ws implementations.

Let assume you defined a web servie method:

public String getSomething() throws ClientException

where ClientException would contain information like exception id and message.

If the method throw an exception, on Flex side you want to extract id and message to present it to user.To do this in Flex we can use standard “fault” handler (I assume you know how to call web service from Flex so I would go into much details there)

  service.doSomething.addEventListener("result", responder.result);
  service.doSomething.addEventListener("fault", responder.fault);
  service.doSomething();

Here we use already defined web service (service) and assign “result” handler to handle normal flow and “fault” handler to handler any exception, including our own ClientException.

responder.fault may look like:

public function fault(info:Object):void {

  var ns:Namespace = new Namespace("http://www.acme.com");
  default xml namespace = ns;

  var xml:XML = new XML(info.fault.faultDetail);

  if (xml.id[0] == "123") {
    show(xml.message[0]);
  }
}

Here we acccess Flex provided info object that includes fault member variable. Using fault var we can access exception details (in this case id and message) and process this data.

So far nothing really special done on Flex side.

Now let’s look how to implement server side. I would not go into details how expose web service in XFire (you can find this info in XFire docs). Instead let’s focus on Flex specific moments. The most important one is that Flex would not process SOAP fault message if HTTP status code other then 200. Here is excerpt from Flex manual:

> “On web browsers, when a service returns any status code other than 200,
> Adobe Flash Player
> cannot read the body of the response. If the status code is 500 and the
> body contains a
> fault, there is no way to get to the fault. The proxy works around this
> issue by forcing the
> status code for faults to 200; the player passes the body of the
> response along with the
> fault intact. ”

It means that we need to add a code on server side to ensure that response code is equal to 200 when we throw an exception. When exception thrown from XFire HTTP status code would be 500 (web services standard implies it), so we need to create a servlet filter that would set HTTP status code to 200 to allow Flex web service engine access SOAP body.

This is also pretty standard stuff, you would need to define filter and associate it with XFire servlet in web.xml (you can find more info on filter here):

<?xml version=“1.0″ encoding=“UTF-8″?>
<web-app xmlns=“http://java.sun.com/xml/ns/j2ee” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” version=“2.4″ xsi:schemaLocation=“http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd”>
  <servlet>
    <servlet-name>XFireServlet</servlet-name>
    <servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>XFireServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
  <filter>
   <filter-name>ExceptionFilter</filter-name>
   <filter-class>com.acme.filter.ExceptionFilter</filter-class>
  </filter>
  <filter-mapping>
   <filter-name>ExceptionFilter</filter-name>
 <servlet-name>XFireServlet</servlet-name>
  </filter-mapping>
</web-app>

Exception filter would like:


import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class ExceptionFilter implements Filter {

  public void destroy() {
  }

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  throws IOException, ServletException {

  HttpServletResponse httpResponse = (HttpServletResponse) response;
  ExceptionHttpServletResponseWrapper wrapper = new ExceptionHttpServletResponseWrapper(httpResponse);

  chain.doFilter(request, wrapper);
}

public void init(FilterConfig arg0) throws ServletException {
}

}

And you to define HttpServletResponsWrapper that would control setStatus() method to ensure that status code is 200.


import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class ExceptionHttpServletResponseWrapper extends
  HttpServletResponseWrapper {

  public ExceptionHttpServletResponseWrapper(HttpServletResponse response) {
   super(response);
  }

  @Override
  public void setStatus(int statusCode) {
   if (statusCode == 500) {
     super.setStatus(200);
   }
  }
}

The above code can be used for any web service implementation.

We still need to add some specific way XFire handles user’s exception. One way to it is to create a custom ClientExceptionDetails that contain id and message.


public class ClientExceptionDetails {

 private int id;
 private String message;

 public ClientExceptionDetails(int id, String message) {
  super();
  this.id = id;
  this.message = message;
 }

 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getMessage() {
  return message;
 }
 public void setMessage(String message) {
  this.message = message;
 }
}

And make sure that ClientException uses this info:


import javax.xml.namespace.QName;

import org.codehaus.xfire.fault.FaultInfoException;
import org.codehaus.xfire.fault.XFireFault;

public class ClientException extends FaultInfoException  {

 private ClientExceptionDetails faultDetail;

 public ClientException(String message, ClientExceptionDetails detail) {
  super(message);

  this.faultDetail = detail;
 }

 public ClientExceptionDetails getFaultInfo() {
  return faultDetail;
 }

 public static QName getFaultName() {
  return new QName(”http://www.acme.com”, “ClientException”);
 }
}

(Check XFire doc for more info about FaultInfoException)

That’s all, now when you throw ClientException, XFire would use ClientExceptionDetails to populat SOAP body with id and message that you can extract later on Flex side from fault.faultDetail.


About this entry