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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26788608/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 03:17:12  来源:igfitidea点击:

Design Pattern to model Request and Response Objects for Webservices

javaweb-servicesrestdesign-patternsjax-rs

提问by DntFrgtDSemiCln

I have about 7 REST web services to implement. Some of these web services have a standard (identical) response, while some have different responses.

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

The requests for these web services are different but some requests and some of the responses have the same underlying data objects.

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

I am not sure if I have to build separate request/response classes for each web service or reuse a standard one. I would like to know if there is a design pattern to model the request objects and response objects for these web services.

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

Ok, say Account and Book are two rest resources my web services will be working on.

好的,假设 Account 和 Book 是我的 Web 服务将处理的两个其余资源。

class Account {
    String username;
    String id;
}


class Book {
    String title;
    String isbn;
}

So my web services look like this:

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

MYAPI/CreateAccountandBook
MYAPI/Account/Create
MYAPI/Book/Create
MYAPI/Book/Update/{isbn}
MYAPI/Account/Update/{id}
MYAPI/Account/getInfo/{id} 

and so on.

等等。

Now CreateAccountandBookrequest will take an account object and a list of books in the payload. Also the response object for MYAPI/Account/getInfo/{id}has an account object and a list of books associated with that account. But the response object also includes a statusCodeand Description.

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

Now I would like to create classes for these request and response objects in the best possible way.

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

Okay for a start.

好的开始。

I have two abstract classes StandardRequestand StandardResponse.

我有两个抽象类StandardRequestStandardResponse.

All requests classes will extend the Standard Request class and customize accordingly. All response classes will extend the Standard response class and customize accordingly.

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

But these requests and response can be way different from each other but still re-use the same entity objects.

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

For instance:

例如:

createAccountandBookrequest object looks like this:

createAccountandBook请求对象如下所示:

class CreateAccountAndBookRequest {
   Account account;
   List<Book> books;
}

while the response for the getInfoweb service is:

getInfoWeb 服务的响应是:

class GetInfoResponse {
   Account account;
   List<Book> books;
   String statusCode;
   String description;
}

so there is overlap across request and response classes. I can create two (req/res) classes for each web service. But would like to know if there is a better way to model these classes.

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

采纳答案by dasm80x86

I had a similar dilemma; I went in the genericdirection and am liking the results; haven't looked back since.

我也有类似的困境;我朝着通用方向前进,并且喜欢结果;从那以后就没有回头。

If I had a GetAccountsAPI method the signature might look like.

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

public final Response<Account[]> getAccounts()

Naturally the same principle may be applied to requests.

自然地,相同的原则可以应用于请求。

public final Response<Account[]> rebalanceAccounts(Request<Account[]>) { ... }

In my opinion; decoupling the individual entities from requests and responses yields a neater domain and object graph.

在我看来; 将单个实体与请求和响应分离会产生一个更整洁的域和对象图。

Below is an example of what such a generic response object might look like. In my case; I'd built the server to have a generic response for all requests to enhance error handling and lower coupling between domain objects and response objects.

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

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;
  }
}

回答by Juan Henao

you can try Hexagonal Architecture, it consist in put your business logic in the middle and access it via rest, soap, smtp, etc.

您可以尝试 Hexagonal Architecture,它包括将您的业务逻辑放在中间并通过 rest、soap、smtp 等访问它。

and in your Rest files you just need to expose the path and call a class with all the things you need to do like a DAO, or something else.

在你的 Rest 文件中,你只需要公开路径并调用一个包含你需要做的所有事情的类,比如 DAO 或其他东西。

if the service refer to the same, you can leave in a single class, or separate depending of the propose of the services.

如果服务指的是相同的,您可以在一个班级中离开,也可以根据服务的建议分开。

For your problem you can create a DTO (Data Transfer Object) with simple data that you need, like account number, books, description, and you can override the toString() to give a proper response or convert the data in a json reponse.

对于您的问题,您可以使用您需要的简单数据创建 DTO(数据传输对象),例如帐号、账簿、描述,并且您可以覆盖 toString() 以提供正确的响应或将数据转换为 json 响应。

you can check http://alistair.cockburn.us/Hexagonal+architecture, i think is the best practice for that

你可以检查http://alistair.cockburn.us/Hexagonal+architecture,我认为这是最好的做法

回答by Juan Henao

 public ResponseDto(){
     String username;
     int id;
     ArrayList books <Book> = new ArrayList<Book>();

    // proper getters and setters...
    @Override
    public String toString(){
       //parse this object to return a proper response to the rest service,
       //you can parse using some JSON library like GSON
    }
}

回答by Luiggi Mendoza

I don't know if there is such design pattern. I do the following:

不知道有没有这样的设计模式。我执行以下操作:

  • For GET requests, define the parameters in query string or in path. Preferred way is path. Also, you will have few parameters for your service. Each service will handle this on its own. There is no reusability here.
  • For POST requests, consume the parameters in JSON format that comes in the body of the request. Also, use an adapter (depending on the technology you're using) that will map the JSON content to a specific class that you receive as parameter.
  • For responses, there are two approaches:

    • You can create a custom ResponseWrapperclass that will be your real response. This will contain the response code, the description and a field called value which stores the real content of the response in case of a success processing of the input data. The class will look like this:

      public class ResponseWrapper {
          private int statusCode;
          private String description;
          private String value;
      }
      

      In this case, String valuewill store the concrete response in JSON format. For example:

      @Path("/yourapi/book")
      public class BookRestfulService {
      
          @POST("/create")
          @Produces("json")
          public ResponseWrapper createBook(Book book) {
              ResponseWrapper rw = new ResponseWrapper();
              //do the processing...
              BookService bookService = new BookService();
              SomeClassToStoreResult result = bookService.create(book);
              //define the response...
              rw.setStatusCode(...);
              rw.setDescription("...");
              rw.setValue( convertToJson(result) );
          }
      
          static String convertToJson(Object object) {
              //method that probably will use a library like Hymanson or Gson
              //to convert the object into a proper JSON strong
          }
      }
      
    • Reuse the HTTP Response Status Code, use 200 (or 201, this depends on the type of request) for successful requests and a proper status code for the response. If your response has status code 200 (or 201) then return the proper object in JSON format. If your response has a different status code, provide a JSON object like this:

      { "error" : "There is no foo in bar." }
      
  • 对于 GET 请求,在查询字符串或路径中定义参数。首选方式是路径。此外,您的服务参数很少。每个服务都会自行处理。这里没有可重用性。
  • 对于 POST 请求,使用请求正文中的 JSON 格式的参数。此外,使用适配器(取决于您使用的技术)将 JSON 内容映射到您作为参数接收的特定类。
  • 对于响应,有两种方法:

    • 您可以创建一个自定义ResponseWrapper类来作为您的真实响应。这将包含响应代码、描述和一个名为 value 的字段,该字段在成功处理输入数据的情况下存储响应的真实内容。该类将如下所示:

      public class ResponseWrapper {
          private int statusCode;
          private String description;
          private String value;
      }
      

      在这种情况下,String value将以 JSON 格式存储具体响应。例如:

      @Path("/yourapi/book")
      public class BookRestfulService {
      
          @POST("/create")
          @Produces("json")
          public ResponseWrapper createBook(Book book) {
              ResponseWrapper rw = new ResponseWrapper();
              //do the processing...
              BookService bookService = new BookService();
              SomeClassToStoreResult result = bookService.create(book);
              //define the response...
              rw.setStatusCode(...);
              rw.setDescription("...");
              rw.setValue( convertToJson(result) );
          }
      
          static String convertToJson(Object object) {
              //method that probably will use a library like Hymanson or Gson
              //to convert the object into a proper JSON strong
          }
      }
      
    • 重用HTTP 响应状态代码,使用 200(或 201,这取决于请求的类型)表示成功的请求和正确的响应状态代码。如果您的响应具有状态代码 200(或 201),则以 JSON 格式返回正确的对象。如果您的响应具有不同的状态代码,请提供如下 JSON 对象:

      { "error" : "There is no foo in bar." }
      

There is a trade off using RESTful services with JSON or XML, and that's the price of complexity for consumers, who may not know the structure of the response. In case of WS-* web services, the trade-off comes in performance terms (compared ton RESTful approach).

使用带有 JSON 或 XML 的 RESTful 服务需要权衡,这对于消费者来说是复杂的代价,他们可能不知道响应的结构。在 WS-* Web 服务的情况下,权衡来自性能方面(与 RESTful 方法相比)。

回答by ekostadinov

About the

有关

if there is a Design Pattern to model the request objects and response objects

如果有一个设计模式来建模请求对象和响应对象

the standard way, you can consider is Command design pattern. Since it allows you to encapsulate a command request as an object. And thereby letting you parameterize clients with different requests, queue or log requests, responses and support undoable operations etc.

标准的方式,你可以考虑的是Command design pattern。因为它允许您将命令请求封装为对象。从而让您参数化具有不同请求、队列或日志请求、响应和支持可撤销操作等的客户端。

As sample implementation:

作为示例实现:

  abstract class Request{
    public abstract void Execute();
    public abstract void UnExecute();
  } 

   class AccountAndBookRequest extends Request{
   Account account;
   List<Book> books;
   }

回答by inor

The big problem I see in all the answers so far including the question is that they all violate the principal of separation of concerns, infomation hiding and encapsulation. In all answers request (and response) classes are tightly coupled to model classes. That is a more serious issue and raises a question more important than the relationship between the requests and responses...

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

What should be the relationship between the request/response classes and the model classes?

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

Since the request class (e.g. CreateBookRequest) and the model class Book have mostly the same data properties, you could do any of the following:

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

A. Put all your data/getters/setters into the Book class and have the CreateBookRequest extend from the class

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

B. Have your CreateBookRequest contain a Book as a member (as in the question and answers given by ekostadinov, Juan Henao, . The generic usage given by dasm80x86 is also a special case of this)

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

C. Put data/getters/setters in BookBase and have both Book and CreateBookRequest extend from BookBase

C. 将 data/getter/setter 放入 BookBase 并从 BookBase 扩展 Book 和 CreateBookRequest

D. Put all/some data/getters/setters in BookStuff and have both Book and CreateBookRequest contain a BookStuff

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

E. Put all the data/getters/setters in both Book and CreateBookRequest. (you can copy-paste).

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

The correct answer is E.We are all so trained and eager to "re-use" that this is the least intuitive answer.

正确答案是 E。我们都受过如此训练并渴望“重用”,因此这是最不直观的答案。

The request class CreateBookRequest (as well as the response class CreateBookResponse) and the model class Book, should NOT be be in same class hierarchy (other than both having Object as a top most parent) (A,C). Also the CreateBookRequest should not refer/contain to the model Book or to any of the composite classes that are members in the Book class (B,D)

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

The reasons for this are as follows:

原因如下:

1) You want to modify the model object or the request object independent of each other. If your request refers to your mdoel (as in A-D) any change in the model will be reflected in the interface, and therefore break your API. Your customers are going to write clients according to the API dictated by your request/response classes and they don't want to change those clients whenever you make a change to your model classes. you want the request/response and the model to vary independently.

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

2) Separation of concerns. Your request class CreateBookRequest may contain all kinds of interface/protocol related annotations and members (e.g. validation annotations that the JAX-RS implementation knows how to enforce). These interface-related annotations should not be in the model object. (as in A)

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

3) from an OO perspective CreateBookRequest is not a Book (not IS_A) nor does it contain a book.

3) 从面向对象的角度来看 CreateBookRequest 不是一本书(不是 IS_A),也不包含一本书。

The flow of control should be as follows:

控制流程应如下所示:

1) The interface/control layer (the one that receives the Rest-API calls) should use as its methods parameters Request/Response classes defined specifically for that layer (e.g. CreateBookRequest). Let the container/infrastructure create those from the REST/HTTP/whatever request.

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

2) The methods in the The interface/control layer should create in some way an instance of a model class object, and copy values from the request classes into the model class object,

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

3) The methods in the The interface/control layer should call a BO/Manager/Whatever (in the model layer... which is responsible for business logic) passing to it the model class object and not the interface class/method parameter class object (in other words, NOT as Luiggi Mendoza has shown in his answer)

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

4) The model/BO method would return some model class object or some "primitive".

4) 模型/BO 方法将返回一些模型类对象或一些“原始”。

5) Now the interface method (the caller) should create an interface class response object, and copy values into it from the model class object returned by the model/BO. (Just like Luiggi Mendoza as shown in his answer)

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

6) The container/infrastructure would then create the JSON/XML/whatever response from the response class object.

6) 然后容器/基础设施将从响应类对象创建 JSON/XML/任何响应。

Now to the question asked... What should be the relationship between the requests and response classes?

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

Request classes should extend from request classes and not extend nor contain response classes, and vice versa. (as was also suggested by question asker). Usually you have a very basic BaseRequest class, extended by something like CreateRequest, UpdateRequest, etc... where properties common to all create requests are in CreateRequest which is then extended by more specific request classes such as CreateBookRequest...
Similarly, but parallel to it, the is the Response class hierarchy.

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

The question asker also asked whether it's ok for both CreateBookRequest and CreateBookResponse to contain the same member such as (Never a model class though!) BookStuffInRequestAndResponse which properties common to both the request and response?

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

This is not as severe an issue as having the request or reponse refer to a class that is also referred to by the model. The problem with this is that if you need to make a change to your API request and make it in BookStuffInRequestAndResponse, it immediately affects your response (and vice versa).

这不像让请求或响应引用模型也引用的类那样严重。这样做的问题是,如果您需要对 API 请求进行更改并在 BookStuffInRequestAndResponse 中进行更改,它会立即影响您的响应(反之亦然)。

It's not so bad because 1) if your customer needs to fix their client code because you change the request parameters, they may as well fix handle/fix the changed response and 2) most likely changes in the request would require change to the response any way (for example adding a new attribute), however, that may not always be the case.

这还不错,因为 1) 如果您的客户因为您更改了请求参数而需要修复他们的客户端代码,他们也可以修复句柄/修复更改后的响应 2) 请求中的更改很可能需要更改响应方式(例如添加新属性),但是,情况可能并非总是如此。