【问题标题】:Document Image response in Spring REST DocsSpring REST Docs 中的文档图像响应
【发布时间】:2017-04-28 12:35:50
【问题描述】:

我有一个生成随机图像的 REST 端点。我正在使用 Spring REST Docs,但响应在 http-response.adoc 文件中都是乱码。 Mock MVC 和 REST Docs 是否有一种简单的方法可以将文件存储在某处,以便我的 .adoc 文件可以引用它?

【问题讨论】:

  • 您是想让图像作为 HTTP 响应 sn-p 的一部分出现在您的文档中,还是希望将图像包含在您的文档中以代替 HTTP 响应 sn-p?
  • 我希望能够将图像作为 HTTP 响应 sn-p 的一部分。
  • 抱歉,我不明白您希望它如何工作。您能否稍微扩展您的问题以详细描述您希望生成的文档看起来像什么?请注意,HTTP 响应 sn-p 包含实际的 HTTP 响应(其标头和正文)。响应体不是图像,它是可以转换成图像的二进制数据。
  • 当我运行生成模拟 MVC 测试的文档时,我得到 .adoc 文件作为输出。例如,get-person/1/http-response.adoc。该文件使用源代码语法。我建议图像的输出可以是名为 http-response.png 的图像字节,或者是一个名为 http-response.adoc 的文件,带有 image:: 参考。该引用将指向 mockMvc 测试生成的图像文件。

标签: spring spring-restdocs


【解决方案1】:

不是完美但可行的解决方案:

class ImageSnippet implements Snippet {

    private final String filePath;

    public ImageSnippet(String filePath) {
        this.filePath = filePath;
    }

    @Override
    public void document(Operation operation) throws IOException {
        byte[] picture = operation.getResponse().getContent();

        Path path = Paths.get(filePath);
        Files.deleteIfExists(path);
        Files.createDirectories(path.getParent());
        Files.createFile(path);

        try (FileOutputStream fos = new FileOutputStream(path.toFile())) {
            fos.write(picture);
        }
    }
}

以及在MockMvc测试中的使用(图片文件夹路径很重要):

    mockMvc.perform(get("/my-profile/barcode")
                      .accept(MediaType.IMAGE_PNG))
            .andExpect(status().isOk())
            .andDo(document("my-profile/barcode",
                      new ImageSnippet("build/asciidoc/html5/images/barcode.png")));

.adoc 模板中:

 image::barcode.png[]

这是我的build.gradle Asciidoctor 配置(imagesdir 很重要):

asciidoctor {
    dependsOn test
    backends = ['html5']
    options doctype: 'book'

    attributes = [
            'source-highlighter': 'highlightjs',
            'imagesdir'         : './images',
            'toc'               : 'left',
            'toclevels'         : 3,
            'numbered'          : '',
            'icons'             : 'font',
            'setanchors'        : '',
            'idprefix'          : '',
            'idseparator'       : '-',
            'docinfo1'          : '',
            'safe-mode-unsafe'  : '',
            'allow-uri-read'    : '',
            'snippets'          : snippetsDir,
            linkattrs           : true,
            encoding            : 'utf-8'
    ]

    inputs.dir snippetsDir
    outputDir 'build/asciidoc'
    sourceDir 'src/docs/asciidoc'
    sources {
        include 'index.adoc'
    }
}

【讨论】:

    【解决方案2】:

    您可以做的是实现一个自定义代码段,以保存结果响应。可以使用接收到的操作的RestDocumentationContext属性来获取输出目录。

        mockMvc.perform(get("/example"))
                .andDo(document("some-example", operation -> {
                    var context = (RestDocumentationContext) operation.getAttributes().get(RestDocumentationContext.class.getName());
                    var path = Paths.get(context.getOutputDirectory().getAbsolutePath(), operation.getName(), "response-file.png");
                    Files.createDirectories(path.getParent());
                    Files.write(path, operation.getResponse().getContent());
                }));
    

    但是,这将在您的输出目录中创建一个 .png 文件,如果您在需要嵌入它的源目录中有 Asciidoc,这通常不是很有用。因此,您可以改为创建一个 Asciidoc 文件,其中包含图像标签的自定义 HTML,其源是响应的 base64 表示。

        mockMvc.perform(get("/example"))
                .andDo(document("some-example", operation -> {
                    var context = (RestDocumentationContext) operation.getAttributes().get(RestDocumentationContext.class.getName());
                    var path = Paths.get(context.getOutputDirectory().getAbsolutePath(), operation.getName(), "response-file.adoc");
                    var outputStream = new ByteArrayOutputStream();
                    outputStream.write("++++\n".getBytes());
                    outputStream.write("<img src=\"data:image/png;base64,".getBytes());
                    outputStream.write(Base64.getEncoder().encode(operation.getResponse().getContent()));
                    outputStream.write("\"/>\n".getBytes());
                    outputStream.write("++++\n".getBytes());
                    Files.createDirectories(path.getParent());
                    Files.write(path, outputStream.toByteArray());
                }));
    

    虽然在空间方面会增加一些开销,但如果您使用它,则无需从源代码中引用构建文件。

    【讨论】:

    • 不错的选择。不过,我不介意从我的 asciidoc 中引用构建文件。我也喜欢能够在构建目录中实际查看 .png 文件,而不必通过 asciidoc 进行渲染。感谢您的回答!
    【解决方案3】:

    如果您不需要/不想在文档中显示图像,另一种方法是:使用 ContentModifyingOperationPreprocessor 将字节替换为一些字符串,让文档的读者清楚地知道会有一些图像响应中的字节数。

    例如:

            mockMvc.perform(get("/api/users/{id}/avatar", user.getId().asString())
                                    .with(createCustomerAuth()))
                   .andExpect(status().isOk())
                   .andDo(document("get-user-avatar-example",
                                   null,
                                   Preprocessors.preprocessResponse(new ContentModifyingOperationPreprocessor(new ContentModifier() {
                                       @Override
                                       public byte[] modifyContent(byte[] originalContent, MediaType contentType) {
                                           return "<< IMAGE BODY HERE >>".getBytes(StandardCharsets.UTF_8);
                                       }
                                   }))));
    

    这会生成一个像这样的adoc 文件:

    [source,http,options="nowrap"]
    ----
    HTTP/1.1 200 OK
    Content-Type: image/png
    Content-Length: 15
    Cache-Control: max-age=3600
    
    << IMAGE BODY HERE >>
    ----
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多