如何将响应实体封装到Swagger中的主模板响应中?

时间:2017-10-12 11:23:20

标签: java json rest jax-rs swagger

我的问题与posted here

有关

我不得不重新解释我的问题,因为我觉得前面的问题太冗长了。再试一次!

我有一个REST API,它返回一个Assets列表,它的编码如下:

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getAllAssets() {
        List<Asset> assets = new ArrayList<Asset>();
        for(int i=1; i<11; i++) {
            assets.add(new Asset(i));
        }

        return RestResponse.create(Status.OK, "10 assets Fetched successfully!", assets);
    }

它产生的响应是这样的:

{
    "message":"10 assets Fetched successfully!",
    "content": [{
        "id":"1",
        "type":"LPTP",
        "owner":"Ram",
        "serialNo":"WDKLL3234K3",
        "purchasedOn":"01 Jan 2017"

    },
    {
        "id":"2",
        "type":"LPTP",
        "owner":"Ram",
        "serialNo":"WDKLL3234K3",
        "purchasedOn":"01 Jan 2017"

    },
    ...
    ]
}

我的应用程序中有60多个服务,它们遵循相同的响应模板:

{
        "message":"Message the service wants to send to the client",
        "content": {
            ....
            Entity returned by the service
            ....
        }
}

以下是代表我们的响应模板的RestResponse POJO:

 public class RestResponse {
        private String message;
        private Object content;

        public String getMessage() {
            return message;
        }
        public void setMessage(String message) {
            this.message = message;
        }
        public Object getContent() {
            return content;
        }
        public void setContent(Object content) {
            this.content = content;
        }

        private RestResponse(String message, Object content) {
            this.message = message;
            this.content = content;
        }

        public static Response create(Response.Status status, String message, Object content) {
            return Response.status(status).entity(new RestResponse(message, content)).build();
        }
}

现在我们正在使用Swagger记录所有API,并遇到了问题。

由于我们为所有API返回RestResponse类,因此我为我的操作编写了以下注释:

@ApiOperation(value="Fetches all available assets", response=RestResponse.class, responseContainer="List")

Swagger所做的是,RestResponse类的Definition模式中的内容如下所示:

"definitions": {
    "RestResponse": {
      "type": "object",
      "properties": {
        "message": {
          "type": "string"
        },
        "content": {
          "type": "object"
        }
      }
    }
 }

在这里,我没有获得有关content属性中对象属性的任何信息或架构。

我理解这是因为Swagger不适用于Generic Objects。

因此,如果我将@ApiOperation注释更改为以下注释:

@ApiOperation(value="Fetches all available assets", response=Asset.class, responseContainer="List")

在上面的例子中,Swagger描述了Asset实体的属性,但显然缺少message属性(我的响应模板)。

我的问题是我想要两者兼而有之。我的响应模板的content属性可以是任何实体。

那么,我可以设置response=Asset.class并指示Swagger在Asset RestResponse属性记录之前将content括起来吗?或者我可以通过其他任何方式实现这一目标?

希望这次我很精确!

谢谢, Sriram Sridharan。

编辑 - 在我尝试了Marc Nuri的建议之后 我使用通用对象创建了类似的RestServiceResponse.class,并将responseReference的{​​{1}}属性设置为@ApiOperation。以下是我得到的JSON。

RestServiceResponse<Asset>

如您所见,{ "swagger": "2.0", "info": { "version": "1.0.0", "title": "" }, "host": "localhost:7070", "basePath": "/assets/rest", "tags": [ { "name": "Assets" } ], "schemes": [ "http" ], "paths": { "/assets/{id}": { "get": { "tags": [ "Assets" ], "summary": "Fetches information about a single asset", "description": "", "operationId": "fetchAssetDetail", "produces": [ "application/json" ], "parameters": [ { "name": "id", "in": "path", "required": true, "type": "integer", "format": "int32" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/RestServiceResponse<Asset>" } } } } } } } 部分完全丢失。我错过了什么吗?

我有project in GitHub,以防您需要查看整个代码。请帮帮我。

2 个答案:

答案 0 :(得分:0)

我们在微服务响应中使用了类似的结构。

唯一的区别是我们的响应容器(即RestResponse)没有原始类型,并且对邮件正文/内容使用了Type参数。

请尝试将您的RestResponse更改为:

public class RestResponse<T> {
    private String message;
    private T content;

    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public T getContent() {
        return content;
    }
    public void setContent(T content) {
        this.content = content;
    }

    private RestResponse(String message, T content) {
        this.message = message;
        this.content = content;
    }

    public static <T> Response create(Response.Status status, String message, Object content) {
        return Response.status(status).entity(new RestResponse(message, content)).build();
    }
}

然后注释你的端点:

 @ApiOperation(
            value="Fetches all available assets",
            responseReference = "RestResponse<List<Asset>>")

这适用于最新版本的swagger。

答案 1 :(得分:0)

再次感谢Marc Nuri!

我可能找到了适用于我的方案的解决方案。这就是我的所作所为。我的RestServiceResponse课程与我的问题中提到的课程相同(最近的编辑),我只是通过添加AssetService这样的static final class类进行了修改:

private static final class AssetResponse extends RestServiceResponse<Asset> {
        private Asset data;
}

完成此操作后,我将@ApiOperation注释更改为:

@ApiOperation(value="Fetches an asset by ID", produces="application/json", response=AssetResponse.class)

现在,基本上做的是,出于纯文档的目的,它似乎只是在编译期间用RestServiceResponse类替换了Asset类中的泛型类型,以便Swagger可以定义物体。

现在当我运行swagger JSON URL时,我得到了一个完美的文档!

{
  "swagger": "2.0",
  "info": {
    "version": "1.0.0",
    "title": ""
  },
  "host": "localhost:7070",
  "basePath": "/assets/rest",
  "tags": [
    {
      "name": "Assets"
    }
  ],
  "schemes": [
    "http"
  ],
  "paths": {
    "/assets/{id}": {
      "get": {
        "tags": [
          "Assets"
        ],
        "summary": "Fetches information about a single asset",
        "description": "",
        "operationId": "fetchAssetDetail",
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "type": "integer",
            "format": "int32"
          }
        ],
        "responses": {
          "200": {
            "description": "successful operation",
            "schema": {
              "$ref": "#/definitions/AssetResponse"
            }
          }
        }
      }
    }
  },
  "definitions": {
    "Asset": {
      "type": "object",
      "required": [
        "name",
        "owner",
        "purchaseDate",
        "type"
      ],
      "properties": {
        "id": {
          "type": "integer",
          "format": "int32",
          "description": "The internal unique ID of the Asset"
        },
        "name": {
          "type": "string",
          "description": "Name of the asset"
        },
        "type": {
          "type": "string",
          "description": "Name of the asset",
          "enum": [
            "Laptop",
            "Desktop",
            "Internet Dongle",
            "Tablet",
            "Smartphone"
          ]
        },
        "owner": {
          "type": "string",
          "description": "ID of the person who owns this asset"
        },
        "purchaseDate": {
          "type": "string",
          "format": "date",
          "description": "Date when this asset was purchased"
        }
      }
    },
    "AssetResponse": {
      "type": "object",
      "properties": {
        "message": {
          "type": "string"
        },
        "content": {
          "$ref": "#/definitions/Asset"
        }
      }
    }
  }
}

我还不知道这是否是最有效的解决方案,但是现在它对我来说已经足够好了。请分享您的想法。