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}还有一个帐户对象和与该帐户关联的书籍列表.但响应对象还包括一个statusCode和Description.
现在我想以最好的方式为这些请求和响应对象创建类.
好的开始.
我有两个抽象类StandardRequest和StandardResponse.
所有请求类都将扩展标准请求类并相应地进行自定义.所有响应类都将扩展标准响应类并相应地进行自定义.
但是这些请求和响应可能彼此不同,但仍然重用相同的实体对象.
例如:
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)请求中的最可能的更改需要更改响应然而,方式(例如添加新属性)可能并非总是如此.
小智 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)
| 归档时间: |
|
| 查看次数: |
41600 次 |
| 最近记录: |