Spring boot 终点“/api文件“;不';无法使用自定义GsonHttpMessageConverter

Spring boot 终点“/api文件“;不';无法使用自定义GsonHttpMessageConverter,spring-boot,kotlin,gson,swagger-ui,openapi,Spring Boot,Kotlin,Gson,Swagger Ui,Openapi,我从Springfox Swagger迁移到Springdoc OpenApi。我在配置中添加了几行关于springdoc的内容: springdoc: 路径匹配:/api/** api文档: 路径:/api文档 大摇大摆的用户界面: 路径:/swagger-ui.html 在配置类MainConfig.kt中,我有以下代码: val customGson:Gson=GsonBuilder() .registerTypeAdapter(LocalDateTime::class.java,Dat

我从Springfox Swagger迁移到Springdoc OpenApi。我在配置中添加了几行关于springdoc的内容:

springdoc:
路径匹配:/api/**
api文档:
路径:/api文档
大摇大摆的用户界面:
路径:/swagger-ui.html
在配置类
MainConfig.kt
中,我有以下代码:

val customGson:Gson=GsonBuilder()
.registerTypeAdapter(LocalDateTime::class.java,DateSerializer())
.registerTypeAdapter(ZoneDateTime::class.java,ZoneDateSerializer())
.AddSerializationExclutionStrategy(AnnotationExclutionStrategy())
.enableComplexMapKeySerialization()
.setPrettyPrinting()
.create()
覆盖fun配置消息转换器(转换器:可变列表){
converters.add(GsonHttpMessageConverter(customGson))
}
当我去http://localhost:8013/swagger-ui.html(在配置中我有
server.port:8013
)页面不会重定向到
swagger ui/index.html?url=/api docs&validatorUrl=
。但这不是我的主要问题:)。当我转到
swagger ui/index.html?url=/api docs&validatorUrl=
时,我得到了包含以下信息的页面:

Unable to render this definition The provided definition does not specify a valid version field. Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: "2.0" and those that match openapi: 3.0.n (for example, openapi: 3.0.0). 我尝试使用默认配置,并注释了
configureMessageConverters()
方法和
\api docs
的结果现在看起来像普通的JSON:

/20191218134933
// http://localhost:8013/api-文件
{
“openapi”:“3.0.1”,
“信息”:{(…)}
}
我记得当我使用Springfox时,序列化出现了问题,我的
customGson
增加了一行:
.registerTypeAdapter(Json::class.java,JsonSerializer{src,u,->JsonParser.parseString(src.value())}

我想知道我是否应该使用特殊的
JsonSerializer
。调试后,我的第一个想法是在
io.swagger.v3.oas.models
包中生成
OpenApi
类。我添加了以下代码:
.registerTypeAdapter(OpenAPI::class.java,JsonSerializer{{uu,{uu,}->JsonParser.parseString(“”)
customGson
中,没有任何更改。。。所以,我在深入挖掘

在我进行招摇过市测试之后:

@EnableAutoConfiguration
@SpringBootTest(webEnvironment=SpringBootTest.webEnvironment.RANDOM\u端口)
@AutoConfigureMockMvc
@ExtendWith(SpringExtension::class)
@ActiveProfiles(“测试”)
类SwaggerIntegrationTest(@Autowired private val mockMvc:mockMvc){
@试验
fun`应该显示大摇大摆的UI页面`(){
val result=mockMvc.perform(MockMvcRequestBuilders.get(“/swagger ui/index.html”))
.andExpect(状态().isOk)
安德烈图恩先生()
assertTrue(result.response.contentAsString.contains(“招摇过市UI”))
}
@已禁用(“重定向不起作用。请稍后检查”)
@试验
fun`应该显示带有重定向的招摇过市UI页面(){
perform(MockMvcRequestBuilders.get(“/swagger ui.html”))
.andExpect(状态().isOk)
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.TEXT\u HTML))
}
@试验
fun`应该得到api文档`(){
mockMvc.perform(MockMvcRequestBuilders.get(“/api文档”))
.andExpect(状态().isOk)
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath(“\$.openapi”).exists())
}
}
我在控制台上看到:

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /api-docs
       Parameters = {}
          Headers = []
             Body = null
    Session Attrs = {}

Handler:
             Type = org.springdoc.api.OpenApiResource
           Method = org.springdoc.api.OpenApiResource#openapiJson(HttpServletRequest, String)
接下来我在
openapirource
中检查
openapiJson
,然后

@操作(隐藏=真)
@GetMapping(value=API\u DOCS\u URL,products=MediaType.APPLICATION\u JSON\u value)
公共字符串openapiJson(HttpServletRequest请求,@Value(API\u DOCS\u URL)字符串apiDocsUrl)
抛出JsonProcessingException{
calculateServerUrl(请求,apiDocsUrl);
OpenAPI=this.getOpenApi();
返回Json.mapper().writeValueAsString(openAPI);
}
好的,杰克逊。。。我通过
@EnableAutoConfiguration(exclude=[(JacksonAutoConfiguration::class)]
禁用了Jackson,因为我(和我的同事)更喜欢GSON,但它没有解释为什么在添加自定义
GsonHttpMessageConverter
后序列化会出错。我不知道我做了什么坏事。这个
openapiJson()
是端点,可能会弄乱一些东西。。。我不知道。我不知道。你有类似的问题吗?你能给我一些建议或暗示吗


注:抱歉我的英语不好:)。

我在一个用Java编写的项目中遇到了同样的问题,我刚刚通过定义一个过滤器,使用Gson格式化我的springdoc openapi json文档,解决了这个问题。我想您可以很容易地将此解决方案移植到Kotlin

@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
        throws IOException, ServletException {
    ByteResponseWrapper byteResponseWrapper = new ByteResponseWrapper((HttpServletResponse) response);
    ByteRequestWrapper byteRequestWrapper = new ByteRequestWrapper((HttpServletRequest) request);

    chain.doFilter(byteRequestWrapper, byteResponseWrapper);

    String jsonResponse = new String(byteResponseWrapper.getBytes(), response.getCharacterEncoding());

    response.getOutputStream().write((new com.google.gson.JsonParser().parse(jsonResponse).getAsString())
            .getBytes(response.getCharacterEncoding()));
}

@Override
public void destroy() {

}

static class ByteResponseWrapper extends HttpServletResponseWrapper {

    private PrintWriter writer;
    private ByteOutputStream output;

    public byte[] getBytes() {
        writer.flush();
        return output.getBytes();
    }

    public ByteResponseWrapper(HttpServletResponse response) {
        super(response);
        output = new ByteOutputStream();
        writer = new PrintWriter(output);
    }

    @Override
    public PrintWriter getWriter() {
        return writer;
    }

    @Override
    public ServletOutputStream getOutputStream() {
        return output;
    }
}

static class ByteRequestWrapper extends HttpServletRequestWrapper {

    byte[] requestBytes = null;
    private ByteInputStream byteInputStream;


    public ByteRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        InputStream inputStream = request.getInputStream();

        byte[] buffer = new byte[4096];
        int read = 0;
        while ((read = inputStream.read(buffer)) != -1) {
            baos.write(buffer, 0, read);
        }

        replaceRequestPayload(baos.toByteArray());
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() {
        return byteInputStream;
    }

    public void replaceRequestPayload(byte[] newPayload) {
        requestBytes = newPayload;
        byteInputStream = new ByteInputStream(new ByteArrayInputStream(requestBytes));
    }
}

static class ByteOutputStream extends ServletOutputStream {

    private ByteArrayOutputStream bos = new ByteArrayOutputStream();

    @Override
    public void write(int b) {
        bos.write(b);
    }

    public byte[] getBytes() {
        return bos.toByteArray();
    }

    @Override
    public boolean isReady() {
        return false;
    }

    @Override
    public void setWriteListener(WriteListener writeListener) {

    }
}

static class ByteInputStream extends ServletInputStream {

    private InputStream inputStream;

    public ByteInputStream(final InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public int read() throws IOException {
        return inputStream.read();
    }

    @Override
    public boolean isFinished() {
        return false;
    }

    @Override
    public boolean isReady() {
        return false;
    }

    @Override
    public void setReadListener(ReadListener readListener) {

    }
}
您还必须仅为文档url模式注册过滤器

@Bean
public FilterRegistrationBean<DocsFormatterFilter> loggingFilter() {
    FilterRegistrationBean<DocsFormatterFilter> registrationBean = new FilterRegistrationBean<>();

    registrationBean.setFilter(new DocsFormatterFilter());
    registrationBean.addUrlPatterns("/v3/api-docs");

    return registrationBean;
}
@Bean
公共过滤器注册Bean loggingFilter(){
FilterRegistrationBean registrationBean=新的FilterRegistrationBean();
setFilter(新的docsformaterfilter());
addUrlPatterns(“/v3/api文档”);
返回注册bean;
}

好的,我试过了,看起来还可以。我对页面有不同的问题-我得到了404:)。当我转到(另一个项目,另一个端口:D)时,我遇到了类似的重定向问题:“无法在名为“dispatcherServlet”的servlet中解析名为“redirect:/api/swagger ui/index.html”的视图?configUrl=/api docs/swagger config”。当我转到这个链接时,我看到:“出现了一个意外错误(type=notfound,status=404)。”
/api docs/
的格式有效(相当JSON),但我没有“/api docs/swagger config”。我将尝试降级Springdoc OpenApi.OK。我添加了“override fun ConfigureViewResolver(注册表:ViewResolverRegistry)=super.ConfigureViewResolver(注册表.apply{viewResolver(InternalResourceViewResolver())}”)来解决重定向问题。此:
@Bean
public FilterRegistrationBean<DocsFormatterFilter> loggingFilter() {
    FilterRegistrationBean<DocsFormatterFilter> registrationBean = new FilterRegistrationBean<>();

    registrationBean.setFilter(new DocsFormatterFilter());
    registrationBean.addUrlPatterns("/v3/api-docs");

    return registrationBean;
}