首页 > 解决方案 > 为了相同的目的,我应该如何设计我的 restfull API 以通过不同格式(JSON、CSV)的 POST 接受数据?

问题描述

我有一个使用 Spring Boot 开发 restfull API 的业务需求,它执行以下操作:-

a) 通过来自客户端的 POST 请求接受 csv 格式的车辆数据。b) 通过来自客户端的 POST 请求接受 JSON 格式的车辆数据。

在上面的 a) 和 b) 中,字段是相同的,只是格式不同(一个是 JSON,另一个是 CSV)。

我的问题是我的设计应该如何实现这一点?

1)我是否应该通过创建类 A 并拥有 2 个不同的端点方法来继续。一个接受csv,另一个接受json?或者有什么更好的方法来处理这种情况?

2)我的班级结构应该是什么样的?

3) 任何适合此要求的特定设计模式?或者有什么具体的建议来处理这种情况?

非常感谢任何帮助。

标签: javarestspring-bootdesign-patternsarchitecture

解决方案


您可以通过为每种格式定义一个Deserializer接口和不同的实现来做到这一点。

问题1:这取决于你想如何做到这一点。一种方法是使用单个端点并使用Web 请求的Content-Type标头并基于它选择Deserializer 。

这是一个例子:

public enum Formats { JSON, CSV, XML }

public interface Deserializer {

  Format getFormat();
  object deserialize(string input);
}

public JSONDeserializer extends Deserializer {

    public Format getFormat() { return Formats.JSON; }
    public object deserialize(string input) { .... }
}

public CSVDeserializer extends Deserializer {
    public Format getFormat() { return Formats.CSV; }
    public object deserialize(string input) { .... }
}

public XMLDeserializer extends Deserializer {
    public Format getFormat() { return Formats.XML; }
    public object deserialize(string input) { .... }
}

如果您想避免使用显式格式枚举来定义您的类型,您可以将内容类型直接用作字符串或 MediaType。这将导致您的 Deserializer 接口与您的请求处理程序紧密耦合,从而难以在代码的其他地方使用它们。此外,作为替代设计,您可以将检查的责任转移到解串器中。

public interface Deserializer {

  string getContentType();
  object deserialize(string input);
}

public JSONDeserializer extends Deserializer {

    public string getContentType() {
        return "application/json";
    }

    public object deserialize(string input) { .... }
}

public interface Deserializer {

  bool cetDeserialize(string contentType);
  object deserialize(string input);
}

public JSONDeserializer extends Deserializer {

    public bool cetDeserialize(string contentType) {
       return contentType == "application/json";
    }

    public object deserialize(string input) { .... }
}

这是你如何使用它。

List<String> mDeserializers = Arrays.asList(
    new JSONDeserializer(), 
    new CSVDeserializer(), 
    new XMLNDeserializer()
);

Deserializer deserializer = mDeserializers
      .stream()
      .filter(d => d.getContentType() == contentType)
      .findFirst();

// or if you use a specific format:
Deserializer deserializer = mDeserializers
      .stream()
      .filter(d => d.getFormat() == getFormatFromContentType(contentType))
      .findFirst();

deserializer.deserialize(input);

至于spring,自己没试过,不用指定consumes也可以这样:

 @RequestMapping(value = "/data", method = RequestMethod.POST)
 public String process(
   @RequestHeader HttpHeaders httpHeaders, 
   @RequestBody String body){
   // get value of content-type header as string
   // get serializer based on string value
 }

或者

 @RequestMapping(value = "/data", method = RequestMethod.POST)
 public String process(
   @RequestHeader(value="Content-Type") String contentType, 
   @RequestBody String body){
   // get serializer based on string value
 }

推荐阅读