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

时间:2014-10-27 20:03:52

标签: java json rest serialization jackson

鉴于以下POJO:

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

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

我有针对远程REST服务的CRUDding Widget实例的以下客户端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.
    }
}

让我们假设我已经找到了将Widget实例序列化/反序列化为JSON的“神奇”。

这个设计很棒,除了,当有一个服务器端Exception,我想要回传给客户端,RESTful。

我的第一个倾向是修改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;           
    }
}

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

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

所以我问:如何在客户端和服务器之间来回传递Widget实例,RESTful,但仍然允许服务器返回NULL并Exceptions / Throwables

2 个答案:

答案 0 :(得分:2)

我建议将模型响应和错误响应分开 - 分离关注点。假设Jersey,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异常映射到一般的Jersey内部服务器错误。

答案 1 :(得分:1)

我想你可能想要使用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();
    }
}