2014-10-27 97 views
1

鑑於以下的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

回答

2

我建議保持一個模型響應和錯誤響應單獨 - 關注點分離。假設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

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(); 
    } 
} 
+0

謝謝@Kescha Skywalker(+1) - 儘管這可能有效,但我還是希望得到一個更爲抽象/通用的方法,這是框架無關的。有任何想法嗎? – smeeb 2014-10-27 23:15:23

+0

嗯,如果你完全不知道,你可以重新實現@Provider邏輯。或者您捕獲服務上方的異常,並將異常直接存儲回您的Widget中,就像您建議的那樣。我喜歡定義一個單獨的錯誤類型,但這取決於您的要求和範圍 – 2014-10-27 23:28:00