设计模式为Web服务建模请求和响应对象

Dnt*_*Cln 29 java rest design-patterns web-services jax-rs

我有大约7个REST Web服务要实现.其中一些Web服务具有标准(相同)响应,而一些响应不同.

对这些Web服务的请求是不同的,但是一些请求和一些响应具有相同的底层数据对象.

我不确定是否必须为每个Web服务构建单独的请求/响应类或重用标准服务.我想知道是否有一个设计模式来为这些Web服务建模请求对象和响应对象.

好吧,说帐户和书籍是我的网络服务将要处理的两个休息资源.

class Account {
    String username;
    String id;
}


class Book {
    String title;
    String isbn;
}
Run Code Online (Sandbox Code Playgroud)

所以我的网络服务看起来像这样:

MYAPI/CreateAccountandBook
MYAPI/Account/Create
MYAPI/Book/Create
MYAPI/Book/Update/{isbn}
MYAPI/Account/Update/{id}
MYAPI/Account/getInfo/{id} 
Run Code Online (Sandbox Code Playgroud)

等等.

现在CreateAccountandBook请求将获取有效负载中的帐户对象和书籍列表.此外,响应对象MYAPI/Account/getInfo/{id}还有一个帐户对象和与该帐户关联的书籍列表.但响应对象还包括一个statusCodeDescription.

现在我想以最好的方式为这些请求和响应对象创建类.

好的开始.

我有两个抽象类StandardRequestStandardResponse.

所有请求类都将扩展标准请求类并相应地进行自定义.所有响应类都将扩展标准响应类并相应地进行自定义.

但是这些请求和响应可能彼此不同,但仍然重用相同的实体对象.

例如:

createAccountandBook 请求对象如下所示:

class CreateAccountAndBookRequest {
   Account account;
   List<Book> books;
}
Run Code Online (Sandbox Code Playgroud)

而对getInfoWeb服务的响应是:

class GetInfoResponse {
   Account account;
   List<Book> books;
   String statusCode;
   String description;
}
Run Code Online (Sandbox Code Playgroud)

所以请求和响应类之间存在重叠.我可以为每个Web服务创建两个(req/res)类.但是想知道是否有更好的方法来模拟这些类.

ino*_*nor 32

到目前为止,我在所有答案中看到的一个大问题是,它们都违反了关注点分离,信息隐藏和封装的原则.在所有答案中,请求(和响应)类与模型类紧密耦合.这是一个更严重的问题,并提出一个比请求和响应之间的关系更重要的问题......

请求/响应类与模型类之间的关系应该是什么?

由于请求类(例如CreateBookRequest)和模型类Book具有大部分相同的数据属性,因此您可以执行以下任何操作:

A.将所有数据/ getter/setter放入Book类,并从类中扩展CreateBookRequest

B.让你的CreateBookRequest包含一本书作为成员(如ekostadinov,Juan Henao给出的问题和答案,.dasm80x86给出的一般用法也是一个特例)

C.在BookBase中放置数据/ getter/setter,并从BookBase扩展Book和CreateBookRequest

D.将所有/一些数据/ getter/setter放入BookStuff并让Book和CreateBookRequest包含BookStuff

E.将所有数据/ getter/setter放入Book和CreateBookRequest中.(你可以复制粘贴).

正确答案是E. 我们都受过如此训练并渴望"重复使用",这是最不直观的答案.

请求类CreateBookRequest(以及响应类CreateBookResponse)和模型类Book不应该位于相同的类层次结构中(除了将Object作为最顶层的父级)(A,C).此外,CreateBookRequest不应引用/包含模型Book或作为Book类(B,D)中成员的任何复合类.

原因如下:

1)您希望彼此独立地修改模型对象或请求对象.如果您的请求涉及您的mdoel(如在AD中),模型中的任何更改都将反映在界面中,从而破坏您的API.您的客户将根据您的请求/响应类所规定的API编写客户端,并且他们不希望在您对模型类进行更改时更改这些客户端.您希望请求/响应和模型独立变化.

2)关注点分离.您的请求类CreateBookRequest可能包含各种与接口/协议相关的注释和成员(例如,JAX-RS实现知道如何强制执行的验证注释).这些与接口相关的注释不应位于模型对象中.(如A)

3)从OO的角度来看,CreateBookRequest不是Book(不是IS_A),也不包含书籍.

控制流程应如下:

1)接口/控制层(接收Rest-API调用的层)应该使用专门为该层定义的Request/Response类作为其方法参数(例如CreateBookRequest).让容器/基础设施从REST/HTTP /任何请求创建.

2)接口/控制层中的方法应该以某种方式创建模型类对象的实例,并将请求类中的值复制到模型类对象中,

3)接口/控制层中的方法应该调用BO/Manager/Whatever(在模型层中...负责业务逻辑)向它传递模型类对象而不是接口类/方法参数类对象(换句话说,不像Luiggi Mendoza在他的回答中所示)

4)model/BO方法将返回一些模型类对象或一些"原始".

5)现在接口方法(调用者)应该创建一个接口类响应对象,并从模型/ BO返回的模型类对象中将值复制到其中.(就像他的回答中所示的Luiggi Mendoza一样)

6)然后容器/基础设施将创建响应类对象的JSON/XML /响应.

现在问的问题是......请求和响应类之间的关系应该是什么?

请求类应该从请求类扩展而不是扩展也不包含响应类,反之亦然.(正如提问者所建议的那样).通常你有一个非常基本的BaseRequest类,由CreateRequest,UpdateRequest等扩展...其中所有创建请求共有的属性都在CreateRequest中,然后由更具体的请求类(如CreateBookRequest)扩展...
类似地,但是并行对它来说,它是Response类层次结构.

问题提问者还询问CreateBookRequest和CreateBookResponse是否可以包含相同的成员,例如(从不使用模型类!)BookStuffInRequestAndResponse请求和响应共有哪些属性?

这不是一个严重的问题,因为请求或响应引用了模型也引用的类.这样做的问题是,如果您需要对API请求进行更改并在BookStuffInRequestAndResponse中进行更改,则会立即影响您的响应(反之亦然).

这并不是很糟糕因为1)如果您的客户需要修改他们的客户端代码,因为您更改了请求参数,他们也可以修复处理/修复已更改的响应; 2)请求中的最可能的更改需要更改响应然而,方式(例如添加新属性)可能并非总是如此.

  • @IbrahimShendy BO = 业务对象。 (2认同)

小智 12

我有类似的困境; 我走向通用的方向,我喜欢结果; 从那以后就没回头了.

如果我有一个GetAccountsAPI方法,签名可能看起来像.

public final Response<Account[]> getAccounts()
Run Code Online (Sandbox Code Playgroud)

当然,相同的原则可以应用于请求.

public final Response<Account[]> rebalanceAccounts(Request<Account[]>) { ... }
Run Code Online (Sandbox Code Playgroud)

在我看来; 将各个实体与请求和响应分离,产生更整洁的域和对象图.

下面是一个这样的通用响应对象可能是什么样子的示例.就我而言; 我构建了服务器以对所有请求进行通用响应,以增强错误处理并降低域对象和响应对象之间的耦合.

public class Response<T> {

  private static final String R_MSG_EMPTY = "";
  private static final String R_CODE_OK = "OK";

  private final String responseCode;
  private final Date execDt;
  private final String message;

  private T response;

  /**
   * A Creates a new instance of Response
   *
   * @param code
   * @param message
   * @param execDt
   */
  public Response(final String code, final String message, final Date execDt) {

    this.execDt = execDt == null ? Calendar.getInstance().getTime() : execDt;
    this.message = message == null ? Response.R_MSG_EMPTY : message;
    this.responseCode = code == null ? Response.R_CODE_OK : code;
    this.response = null;
  }

  /**
   * @return the execDt
   */
  public Date getExecDt() {

    return this.execDt;
  }

  /**
   * @return the message
   */
  public String getMessage() {

    return this.message;
  }

  /**
   * @return the response
   */
  public T getResponse() {

    return this.response;
  }

  /**
   * @return the responseCode
   */
  public String getResponseCode() {

    return this.responseCode;
  }

  /**
   * sets the response object
   *
   * @param obj
   * @return
   */
  public Response<T> setResponse(final T obj) {

    this.response = obj;
    return this;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 您说“域对象和响应对象之间的较低耦合”。这是一个不错的目标,但是您的响应不正确Response &lt;Account&gt;与模型对象Account紧密耦合。帐户的任何更改都会立即影响流式传输给用户的响应。如果您拥有发布响应内容的API,则更改模型对象Account会破坏您的API,而您的客户将不希望这样做。 (3认同)