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异常映射到一般泽西岛内部服务器错误。
我想你可能想使用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();
}
}
谢谢@Kescha Skywalker(+1) - 尽管这可能有效,但我还是希望得到一个更为抽象/通用的方法,这是框架无关的。有任何想法吗? – smeeb 2014-10-27 23:15:23
嗯,如果你完全不知道,你可以重新实现@Provider逻辑。或者您捕获服务上方的异常,并将异常直接存储回您的Widget中,就像您建议的那样。我喜欢定义一个单独的错误类型,但这取决于您的要求和范围 – 2014-10-27 23:28:00
谢谢@starver(+1) - 404响应将JSON仍包含“实体”('e.getMessage()')? – smeeb 2014-10-28 10:06:34
是的。客户端的Response对象将包含状态码和实体。当客户端没有收到成功代码时,他们可以通过Response.readEntity(String.class)来读取你的错误信息。通常,错误响应对象是字符串,键值对的映射或定制的丰富错误对象,具体取决于API的健壮性。无论您选择哪种方式,错误响应实体都应该在整个API中保持一致。 – 2014-10-28 19:58:47