REST错误响应和客户端服务器POJO系列化

REST错误响应和客户端服务器POJO系列化

问题描述:

鉴于以下的POJOs:REST错误响应和客户端服务器POJO系列化

public class BaseEntity { 
    public Long id; 
    // ctor, getter & setter 
} 

public class Widget extends BaseEntity { 
    public String fizz; 
    public Boolean isBuzz; 
    // ctor, getters & setters 
} 

我有CRUDding Widget情况下,对远程REST服务以下客户端API:

public class WidgetServiceClient { 
    public void createWidget(Widget w) { 
     // RESTful call: POST localhost/widgets 
    } 

    public Widget getWidgetById(Long id) { 
     // RESTful call: GET localhost/widgets/{id} 
    } 
} 

而下面暴露的RESTful服务端点:

public class WidgetService { 
    // Web server passes POST localhost/widgets/ calls to this method. 
    public Widget createWidget(Widget w) { 
     // Create the 'w' widget in the DB and return it with its DB-generated ID. 
    } 

    // Web server passes GET localhost/widgets/{id} calls to this method. 
    public Widget getWidgetById(Long id) { 
     // Ask the DB for the Widget with the passed-in 'id'. If it exist return it. 
     // Otherwise return NULL. 
    } 
} 

让我们假装我'我已经找出了向/从JSON序列化/反序列化实例的“魔术”。

这个设计很好,,除了,当有一个服务器端Exception,我想通过RESTfully回传到客户端。

我的第一个倾向是修改BaseEntity有一个Throwable可用于通讯服务器端错误返回给客户端:

public class BaseEntity { 
    public Long id; 
    public Throwable error; 
    // ctor, getters & setters 
} 

那么接下来:

public class WidgetService { 
    // Web server passes POST localhost/widgets/ calls to this method. 
    public Widget createWidget(Widget w) { 
     try { 
      // Create the 'w' widget in the DB and return it with its DB-generated ID. 
     } catch(Throwable t) { 
      w.setError(t); 
     } 

     return w;   
    } 

    // Web server passes GET localhost/widgets/{id} calls to this method. 
    public Widget getWidgetById(Long id) { 
     Widget w = new Widget(); 
     try { 
      // Ask the DB for the Widget with the passed-in 'id'. If it exist return it. 
      // Otherwise return NULL. 
     } catch(Throwable t) { 
      w.setError(t); 
     } 

     return w;   
    } 
} 

但这感觉很糟糕/哈克,我想知道其他的Javaland居民是否已经找到了解决这个问题的更好方法/策略。我发生将使用泽西&杰克逊REST /序列化,但我认为该解决方案应该可能是框架无关。

当服务返回NULL时它也没有帮助,这可能发生。

于是我问:如何传递Widget实例来回客户端和服务器之间的REST风格,但仍允许服务器返回空值和Exceptions/Throwables

我建议保持一个模型响应和错误响应单独 - 关注点分离。假设Jersey,泽西了解如何从WebApplicationExceptions中吸取回应,从而允许您在错误响应中提供丰富的错误信息,以帮助您的客户理解错误。

作为一个简单示例,您可以将签名留作返回Widget,并抛出WebApplicationException派生类出错。您的客户将获得成功的200 Widget和异常(例如下面)的404响应。

// Web server passes GET localhost/widgets/{id} calls to this method. 
public Widget getWidgetById(Long id) { 
    Widget w = new Widget(); 
    try { 
     // Ask the DB for the Widget with the passed-in 'id'. If it exist return it. 
     // Otherwise return NULL. 
    } catch(NotFoundException e) { 
     throw new NotFoundException(Response.status(Response.Status.NOT_FOUND) 
       .entity("Widget " + id + " not found.").build()); 
    } catch(Exception e) { 
     throw new WebApplicationException(Response 
       .status(Response.Status.INTERNAL_SERVER_ERROR) 
       .entity("I don't know what happened, but trying again won't help: " 
         + e.getMessage()) 
       .build()); 

    } 

    return w;   
} 

注:仅除非你自定义一个ExceptionMapper

注意响应返回给客户端:不是赶上的Throwable的,如果你独立完成特定的异常代码将更具可读性。在上面,我已经将每个Java异常映射到一般泽西岛内部服务器错误。

+0

谢谢@starver(+1) - 404响应将JSON仍包含“实体”('e.getMessage()')? – smeeb 2014-10-28 10:06:34

+0

是的。客户端的Response对象将包含状态码和实体。当客户端没有收到成功代码时,他们可以通过Response.readEntity(String.class)来读取你的错误信息。通常,错误响应对象是字符串,键值对的映射或定制的丰富错误对象,具体取决于API的健壮性。无论您选择哪种方式,错误响应实体都应该在整个API中保持一致。 – 2014-10-28 19:58:47

我想你可能想使用JAXRS机制@Provider:JAX-RS jersey ExceptionMappers User-Defined Exception

@Provider 
public class UserNotFoundMapper implements 
    ExceptionMapper<UserNotFoundException> { 
    @Override 
    public Response toResponse(UserNotFoundException ex) { 
     return Response.status(404).entity(ex.getMessage()).type("text/plain") 
      .build(); 
    } 
} 
+0

谢谢@Kescha Skywalker(+1) - 尽管这可能有效,但我还是希望得到一个更为抽象/通用的方法,这是框架无关的。有任何想法吗? – smeeb 2014-10-27 23:15:23

+0

嗯,如果你完全不知道,你可以重新实现@Provider逻辑。或者您捕获服务上方的异常,并将异常直接存储回您的Widget中,就像您建议的那样。我喜欢定义一个单独的错误类型,但这取决于您的要求和范围 – 2014-10-27 23:28:00