使用Swagger UI记录包装的REST响应

11

我有一个WidgetDto,我用swagger UI注解对其进行了注释。最终响应将一组WidgetDtos与元数据(根据this RESTful最佳实践文档的第21页)进行了包装。例如:

{
  "data" : [ 
    {
      "id" : 1234,
      "prop1" : "val1"
      ...
    },
    {
      "id" : 5678,
      "prop1" : "val2"
      ...
    }, 
    ...
  ]
}

我的Java代码看起来像这样:

@GET
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(
        value = "Get all widgets.",
        response = WidgetDto.class
)
@ApiResponses(value = {
        @ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
  List<WidgetDto> widgets;
  ...
  Map<String, Object> responseBody = new HashMap<>();
  responseBody.put("data", widgets);
  return Response.ok(responseBody).build();
}

我希望能在多个资源上重用这种模式,并且不想为每种响应类型创建列表DTO。有没有一种优雅的方法可以使用Swagger来记录这些类型的响应主体?


你能否创建一个包装类,该类在字段“data”中表示一个WidgetDto列表? - tim_yates
@tim_yates 是的,但我希望有一种可重用的解决方案,不需要为每个DTO类型创建特定的包装类。 - Bill
3个回答

1

您的元数据不是资源的一部分,而是资源表示的一部分。

在我的情况下,响应类型为 'application/hal+json' 'application/json',它们每个都使用不同的包装器和不同的元数据。 为了解决这个问题,我创建了一个外部文档来解释这两个包装器以及它们各自如何使用元数据表示单个资源和资源列表。

我认为我的选择是正确的,因为我将资源与其表示分开(参见this RESTful最佳实践文档第7页“通过表示操作资源”)。

在您的情况下,您返回WidgetDtos列表,元数据层是资源表示的一部分。

但是,您可以使用像spring-hateoas中使用的Resource和Resources这样的通用类:

public class Resources<T> implements Iterable<T>  {
    private final Collection<T> content;
    Resources(Iterable<T> content) {
        this.content = new ArrayList<T>();
        for (T element : content) {
            this.content.add(element);
        }
    }
}

然后像这样使用它:
@GET
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(
        value = "Get all widgets.",
        response = WidgetDto.class
)
@ApiResponses(value = {
        @ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
  List<WidgetDto> widgets;
  ...
  return Response.ok(new Resources<WidgetDto>(widgets)).build();
}

0

几个月前,我在为学校开发项目时遇到了类似的问题。解决方案是创建一个信封并始终返回它。该信封将包含一个名为"data"的字段,它是通用的;因此,您将能够将其绑定到任何数据类型。

请注意,即使我使用了它,但后来我读到应该很少使用它(我认为您的情况是使用的好例子),但从技术上讲,如果请求失败,则应抛出异常对象。

无论如何,这是我用来返回所有响应的Response类:

public class Response <AnyData> {

    private static final String SUCCESS = "success";
    private static final String FAILURE = "failure";

    private String status;
    private AnyData data;
    private String error;

    private Response(String status, AnyData data, String error) {
        this.status = status;
        this.data = data;
        this.error = error;;
    }

    private Response(String status, AnyData data) {
        this(status, data,"");
    }

    private Response(String status, String error) {
        this(status, null, error);
    }

    public static <AnyData> Response<AnyData> success(AnyData data) {
        return new Response<AnyData>(SUCCESS, data);
    }

    public static <AnyData> Response<AnyData> failure(String error) {
        return new Response<AnyData>(FAILURE, error);
    }

    public static <AnyData> Response<AnyData> unimplemented() {
        return new Response<AnyData>(FAILURE, "Missing implementation in the backend.");
    }

    public static <AnyData> Response<AnyData> failureUserNotFound() {
        return  Response.failure("User not found!");
    }

    public static <AnyData> Response<AnyData> failureBusinessNotFound() {
        return  Response.failure("Business not found!");
    }

    // Removed getters and setters for simplicity.
}

设置完成后,我们将直接从Comtroller创建响应。 我稍微更改了它,以使其与示例一起工作,应该足够易读。 请注意,我为我的响应编写了静态方法:“success()”、“error()”...

@RestController
@Api(tags={"Widgets"})
public class WidgetController {

    @RequestMapping(value="/api/widgets", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Get all widgets.")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Returns the list of widgets.")
    })
    public Response<List<WidgetDto>> getWidgets() {
      List<WidgetDto> widgets = new LinkedList<>();
      widgets.add(new WidgetDto(1234, "val1"));
      widgets.add(new WidgetDto(5678, "val2"));

      return  Response.success(widgets);
    }
}

这是响应正文的示例: {{link1:输入图像描述}}
希望这可以帮到你。

0

您可以在@ApiOperation注释中定义responseContainer属性。

List将使用容器包装您的WidgetDto

@ApiOperation(
    value = "Get all widgets.",
    response = WidgetDto.class,
    responseContainer = "List"
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接