大摇大摆地从属性文件中读取文档

swagger read documentation from properties file


我试图让 Swagger 从属性文件 swagger.properties 中读取 API 文档,但不能。在 @ApiOperation 注释中有一条错误消息:Attribute value must be constant。关于如何解决此问题并能够从属性文件中读取文档的任何建议?
这是控制器代码:

package com.demo.student.demo.controller;

import com.demo.student.demo.entity.Student;
import com.demo.student.demo.service.StudentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping(value = "/v1/students")
@Api(description = "Set of endpoints for Creating, Retrieving, Updating and Deleting of Students.")
public class StudentController {
    private final String message;

    public StudentController(@Value("${test.swagger.message}") String message){
        this.message=message;
    }

    @Autowired
    private StudentService studentService;

    @GetMapping
    @ApiOperation(message)
    public List<Student> findAll(){
        return studentService.findAl();
    }

}

另外,我怎样才能在@API(描述)的class级别注入一个值?

正如错误消息所说,属性值必须是常量,Spring 无法将值注入静态最终字段。同样,也不可能在 class 级别之外注入值(即 @Api 注释的描述)

一种解决方法是创建一个 class,其中只有常量,这些常量都是 final static Strings,就像这样

public final class Constants {
  public static final String API_DESCRIPTION = "description";
  public static final String FIND_ALL_MESSAGE= "message";
}

并在控制器中使用它

@Api(description = Constants.API_DESCRIPTION)
public class StudentController {
   @ApiOperation(Constants.FIND_ALL_MESSAGE)
    public List<Student> findAll(){...}
}

然而,对于 Swagger,对于某些字段,可以使用 ${key} 语法。根据 https://springfox.github.io/springfox/docs/current/#property-file-lookup 的文档,自版本 2.7 以来,以下字段是可能的:

@ApiParam#value()
@ApiImplicitParam#value()
@ApiModelProperty#value()
@ApiOperation#value()
@ApiOperation#notes()
@RequestParam#defaultValue()
@RequestHeader#defaultValue()

有一个解决方法。但是你需要一个额外的依赖 - springfox.

您可以编写一个插件,将来自外部文件的文本注入您的 @ApiOperation description 字段。我在我的项目中使用它来注入降价文件。它非常方便,因为 Swagger 支持降价,并且每个端点都有一个单独的文件,让您有机会编写大量 API 描述(如果您使用的是 IntelliJ IDEA 或类似软件,也可以在降价编辑器中)。

这是您需要的代码:

  1. 自定义注释 (@ApiDescription) 为每个要提供描述的端点。注释的值将是您的降价文件或属性文件的文件路径。稍后插件将在提供的文件路径中查找文件并将描述设置为文件的内容。

    @Target({ ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ApiDescription {
        String value() default "";
    }
    
  2. 插件本身。它是一个扩展点。在这种情况下,我们希望稍后交换或设置 @ApiOperation 注释的描述值。查看 Springfox Plugins.

    ...
    
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spi.service.OperationBuilderPlugin;
    import springfox.documentation.spi.service.contexts.OperationContext;
    import springfox.documentation.spring.web.DescriptionResolver;
    
    ...
    
    @Component
    public class ApiDescriptionPlugin implements OperationBuilderPlugin {
    
        private final DescriptionResolver resolver;
    
        @Autowired
        public ApiDescriptionPlugin(DescriptionResolver resolver) {
            this.resolver = resolver;
        }
    
        @Override
        public void apply(OperationContext context) {
    
            Optional<ApiDescription> descOptional = context.findAnnotation(ApiDescription.class);
            boolean hasText = descOptional.isPresent() && StringUtils.hasText(descOptional.get().value());
            if(!hasText) {
                return;
            }
    
            final String file = descOptional.get().value();
            final URL url = Resources.getResource(file);
    
            String description;
            try {
                description = Resources.toString(url, StandardCharsets.UTF_8);
            } catch(IOException e) {
                log.error("Error while reading markdown description file {}", file, e);
                description = String.format("Markdown file %s not loaded", file);
            }
            context.operationBuilder().notes(resolver.resolve(description));
        }
    
        @Override
        public boolean supports(DocumentationType type) {
            return true;
        }
    }
    
  3. 只需用@ApiDescription("/notes/auth/login.md")注释端点(文件必须在resources文件夹中)

您可以调整此示例以使用属性文件(我不知道您的结构是什么样子以及如何分隔不同的 API 描述)。这种使用 markdown 文件的解决方法对于编写大量描述并使它们远离实际代码很有用。

它适用于 Swagger 2.0

试一试。