简体   繁体   English

文档分层 JSON 有效负载 Spring REST 文档

[英]document hierarchical JSON payload with Spring REST Docs

I started to use Spring REST Docs to document a simple REST API. I have a payload that has some hierarchical structure, for example like this (company with employees).我开始使用Spring REST Docs来记录一个简单的 REST API。我有一个具有某种层次结构的有效载荷,例如像这样(有员工的公司)。

{
    "companyName": "FooBar",
    "employee": 
    [
        {
            "name": "Lorem",
            "age": "42"
        },

        {
            "name": "Ipsum",
            "age": "24"
        }
    ]
}

I would like to separate the documentation of the company object (name and array of employees) and the employee object (employee name and age).我想将公司 object(员工姓名和数组)和员工 object(员工姓名和年龄)的文件分开。

Using the org.springframework.restdocs.payload.PayloadDocumentation.responseFields like explained here forces me to document all fields but in case I only want to document the employee fields - how can I achieve this?这里解释的那样使用org.springframework.restdocs.payload.PayloadDocumentation.responseFields迫使我记录所有字段,但如果我只想记录员工字段 - 我该如何实现呢?

I have no problem to document the company without the employee details because if a field is document the descendants are treated as been documented also.我可以在没有员工详细信息的情况下记录公司,因为如果一个字段是文档,则后代也被视为已记录。 But I can not document the employee structure on its own and I have no dedicated payload for this structure without the company root object.但是我无法单独记录员工结构,并且在没有公司根 object 的情况下,我没有专门用于此结构的有效载荷。

Inspired by this question, I've implemented an enhancement which makes the original answer (see below) obsolete. 受这个问题的启发,我实施了一项增强功能,使原来的答案(见下文)过时了。

If you use 1.0.0.BUILD-SNAPSHOT (available from https://repo.spring.io/libs-snapshot ), you can now mark a field as ignored. 如果使用1.0.0.BUILD-SNAPSHOT(可从https://repo.spring.io/libs-snapshot获得 ),则现在可以将字段标记为已忽略。 Ignored fields count has having been documented without actually appearing in the documentation. 已记录忽略的字段数,但实际上没有出现在文档中。

Given that you want to separate the documentation, having two document calls makes sense. 鉴于您想要分离文档,有两个文档调用是有道理的。 In the first you can document the company name and the array of employees. 首先,您可以记录公司名称和员工阵列。 In the second you document the employees array and mark the company name as ignored. 在第二个中,您记录了employees数组并将公司名称标记为已忽略。

Your test would look something like this: 您的测试看起来像这样:

mockMvc.perform(get("/company/5").accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andDo(document("company",
                responseFields(
                        fieldWithPath("companyName").description(
                                "The name of the company"),
                        fieldWithPath("employee").description(
                                "An array of the company's employees"))))
        .andDo(document("employee",
                responseFields(
                        fieldWithPath("companyName").ignored(),
                        fieldWithPath("employee[].name").description(
                                "The name of the employee"),
                        fieldWithPath("employee[].age").description(
                                "The age of the employee"))));

You'll end up with two directories of snippets, one named company and one named employee . 您将最终得到两个片段目录,一个名为company ,另一个名为employee You can then use the response-fields.adoc snippet from each. 然后,您可以使用每个的response-fields.adoc片段。

Original answer 原始答案

There's no explicit support for ignoring a field when you're documenting a request or a response, but I think you can probably achieve what you want by using a preprocessor to remove the fields that you don't want to document. 当您记录请求或响应时,没有明确支持忽略字段,但我认为您可以通过使用预处理器删除您不想记录的字段来实现所需。

Given that you want to separate the documentation, having two document calls makes sense. 鉴于您想要分离文档,有两个document调用是有道理的。 In the first you can document the company name and the array of employees. 首先,您可以记录公司名称和员工阵列。 In the second you need to preprocess the request to remove the company and then document the employees array. 在第二步中,您需要预处理请求以删除公司,然后记录employees数组。

Your test would look something like this: 您的测试看起来像这样:

mockMvc.perform(get("/company/5").accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andDo(document("company",
                responseFields(
                        fieldWithPath("companyName").description(
                                "The name of the company"),
                        fieldWithPath("employee").description(
                                "An array of the company's employees"))))
        .andDo(document("employee",
                preprocessResponse(removeCompany()),
                responseFields(
                        fieldWithPath("employee[].name").description(
                                "The name of the employee"),
                        fieldWithPath("employee[].age").description(
                                "The age of the employee"))));

Note the use of preprocessResponse in the second document call. 请注意在第二个document调用中使用preprocessResponse removeCompany returns a preprocessor that uses a custom ContentModifier to remove company name from the response: removeCompany返回一个预处理器,它使用自定义ContentModifier从响应中删除公司名称:

private OperationPreprocessor removeCompany() {
    return new ContentModifyingOperationPreprocessor(new ContentModifier() {

        @Override
        public byte[] modifyContent(byte[] originalContent, MediaType contentType) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                Map<?, ?> map = objectMapper.readValue(originalContent, Map.class);
                map.remove("companyName");
                return objectMapper.writeValueAsBytes(map);
            }
            catch (IOException ex) {
                return originalContent;
            }
        }

    });
}

You'll end up with two directories of snippets, one named company and one named employee . 您将最终得到两个片段目录,一个名为company ,另一个名为employee You can then use the response-fields.adoc snippet from each. 然后,您可以使用每个的response-fields.adoc片段。

While the above will work, it's harder than it needs to be. 虽然上述方法有效,但它比它需要的更难。 I've opened an issue so that the preprocessing to modify the response's content will no longer be necessary. 我已经打开了一个问题,因此不再需要修改响应内容的预处理。

This answer is for those who came here at the present time with the same question.这个答案是为那些现在带着同样的问题来到这里的人准备的。 Using version 2.0.x of Spring Rest Docs, you can do the following:使用 Spring Rest 文档的2.0.x版本,您可以执行以下操作:

  1. Document the first level with PayloadDocumentation.relaxedResponseFields methods (Any undocumented field will be ignored):使用PayloadDocumentation.relaxedResponseFields方法记录第一级(任何未记录的字段将被忽略):
  2. Document the second level with PayloadDocumentation.beneathPath that will extract the subsection of the JSON payload found beneath the given path使用PayloadDocumentation.beneathPath记录第二层,它将提取在给定路径下找到的 JSON 有效负载的子部分
  3. This will generate a response-fields.snippet file and a response-fields-beneath-employee.snippet file这将生成一个response-fields.snippet文件和一个response-fields-beneath-employee.snippet文件
.consumeWith(document("company",
  PayloadDocumentation.relaxedResponseFields(
    PayloadDocumentation.fieldWithPath("companyName").description("The name of the company"),
    PayloadDocumentation.fieldWithPath("employee").description("An array of the company's employees")
      ),
  //Using PayloadDocumentation.beneathPath(..) to document separately employee parts
  PayloadDocumentation.responseFields(
    PayloadDocumentation.beneathPath("employee"),
    PayloadDocumentation.fieldWithPath("employee.name").description("The name of the employee"),
    PayloadDocumentation.fieldWithPath("employee.age").description("The age of the employee")
  )
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM