-
-
Notifications
You must be signed in to change notification settings - Fork 523
Malformed api-docs JSON when custom HttpMessageConverter is used #624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Only one If you can remove @EnableWebMvc from your sample, it will work directly. @Component
public class OpenApiResource extends org.springdoc.webmvc.api.OpenApiResource {
public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder, GenericResponseBuilder responseBuilder, OperationBuilder operationParser, RequestMappingInfoHandlerMapping requestMappingHandlerMapping, Optional<ActuatorProvider> servletContextProvider, Optional<List<OpenApiCustomiser>> openApiCustomisers, SpringDocConfigProperties springDocConfigProperties, Optional<SecurityOAuth2Provider> springSecurityOAuth2Provider) {
super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, requestMappingHandlerMapping, servletContextProvider, openApiCustomisers, springDocConfigProperties, springSecurityOAuth2Provider);
}
@Operation(hidden = true)
@GetMapping(value = API_DOCS_URL, produces = MediaType.TEXT_PLAIN_VALUE)
public String openapiJson(HttpServletRequest request, @Value(API_DOCS_URL) String apiDocsUrl)
throws JsonProcessingException {
calculateServerUrl(request, apiDocsUrl);
OpenAPI openAPI = this.getOpenApi();
return Json.mapper().writeValueAsString(openAPI);
}
private void calculateServerUrl(HttpServletRequest request, String apiDocsUrl) {
String requestUrl = decode(request.getRequestURL().toString());
String calculatedUrl = requestUrl.substring(0, requestUrl.length() - apiDocsUrl.length());
openAPIBuilder.setServerBaseUrl(calculatedUrl);
}
} OpenApiResource, will return TEXT_PLAIN_VALUE starting from the next release v1.3.5, so you won't need to override it for this use case. |
@bnasslahsen The fix provided in ee08333 is breaking all our automated tests for Swagger endpoints in all services. Is it possible to leave content-type to be json as it was previously, but instead of returning raw string-- return some dto, so that spring does all json conversion? |
Hi @Aloren, Changing the return type to DTO, is not easy. It require a custom serializer, which is already provided by swagger-core. |
We can of course add additional workarounds for our tests, but the main issue is that such response format violates HTTP specs. With such change content negotiation is completely broken and whoever knows what functionality it might break in the future. This is a type of fix that gives short-term benefits, but may lead to interesting new issues :) |
I would say that the impact should be limited because the return type was already of type String. |
This change is reverted back, as it seems more going to cause problems than resolving them. You can override the OpenApiResource as proposed earlier. This seems the less harmful solution |
its now available on v1.3.7. |
@bnasslahsen I'm using 1.3.9 but I have still the same problem???
With SpringFox I can register an Adapter: springfox/springfox#2758... |
springdoc-openapi is built on top of swagger-core, which is based on jackson; As described before, you can override the OpenApiResource as follow: @Component
public class OpenApiResource extends org.springdoc.webmvc.api.OpenApiResource {
public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder, GenericResponseBuilder responseBuilder, OperationBuilder operationParser, RequestMappingInfoHandlerMapping requestMappingHandlerMapping, Optional<ActuatorProvider> servletContextProvider, Optional<List<OperationCustomizer>> operationCustomizers, Optional<List<OpenApiCustomiser>> openApiCustomisers, SpringDocConfigProperties springDocConfigProperties, Optional<SecurityOAuth2Provider> springSecurityOAuth2Provider, Optional<RouterFunctionProvider> routerFunctionProvider) {
super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, requestMappingHandlerMapping, servletContextProvider, operationCustomizers, openApiCustomisers, springDocConfigProperties, springSecurityOAuth2Provider, routerFunctionProvider);
}
@Operation(hidden = true)
@GetMapping(value = Constants.API_DOCS_URL, produces = MediaType.TEXT_PLAIN_VALUE)
public String openapiJson(HttpServletRequest request, @Value(Constants.API_DOCS_URL) String apiDocsUrl)
throws JsonProcessingException {
calculateServerUrl(request, apiDocsUrl);
OpenAPI openAPI = this.getOpenApi();
return Json.mapper().writeValueAsString(openAPI);
}
} |
Hello, I have incorporated into my project version 1.3.9 OpenAPI to migrate from SpringFox and I have suffered the same problem that is discussed in this entry
We are removing the Jackson AutoConfiguration class from startup and manually configuring the HttpMessageConverter we need
Should I inject the Jackson ObjectMapper into a class that inherits from 'OpenApiResource' and do the JSON conversion in the openApi method ? Or how should I deal with this problem? |
If you are using jackson, there is no need by default to override OpenApiResource, unless your are adding a custom HttpMessageConverter, which changes the default jackson behaviour. You can find attached a sample code using: @SpringBootApplication(exclude = {JacksonAutoConfiguration.class}) If you are still having issues. Please make sure you provide a minimal reproducible example as the one i have attached. |
Thank you very much for the response and the example provided. I have reduced the project to the minimum to show you the problem, I would ask you please if you can verify what is wrong in this example project. I have left a simple controller and the WebConfig class with the ObjectMapper that we use the project. Thank you very much for your attention, regards |
You need to add StringHttpMessageConverter to your method configureMessageConverters, @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter(provideObjectMapper()));
converters.add(new ByteArrayHttpMessageConverter());
} You can find attached, the working example from the source that you have provided. |
Hello, The endpoint returns a ResponseEntity of type "String". The client requests a application/json. So the string must correctly be a JSON string, meaning that it's quoted accordingly. Adding StringHttpMessageConverter (which is by default added with spring boot) changeds this behaviour in an inconsistent way: If the response entity is a string, it is returned as unquoted string (which is, in most cases, invalid json). Returning anything else will be encoded correctly as JSON. So StringHttpMessageConverter introduces buggy behaviour. So for the following endpoint, the behaviour differs with pressence/absence of StringHttpMessageConverter:
This will always produce text/json. With StringHttpMessageConverter enabled, the response will be an invalid JSON of If an endpoint creates a String that is already JSON, It must not rely on that inconsistent behaviour to hopefully deliver this unquoted to the client. A valid implementation of auch an endpoint could look that like:
This endpoint will always return Edit: it would even better to directly write the JSON directly to HttpServletResponse do de-couple the behaviour from specific message converters. |
the new version tweak the response type to MediaType.APPLICATION_JSON_VALUE, but the result string still be eacaped the second time. The final result was the escaped version. |
@jiangxiaoqiang |
Very strange... try same thing, I used this example project |
When custom HttpMessageConverter is used malformed api-docs endpoint produces malformed JSON
Steps to reproduce the behavior:
org.springdoc.webmvc.api.OpenApiResource#openapiJson
. The original response is wrapped with a leading and trailing double quote and all the double internal double quotes are escaped:"{\"openapi\":\"3.0.1\",\"info\":{\"title\":\"OpenAPI definition\",\"version\":\"v0\"},\"servers\":[{\"url\":\"http://localhost:8080\",\"description\":\"Generated server url\"}],\"paths\":{\"/get\":{\"get\":{\"tags\":[\"controller\"],\"operationId\":\"getSomeMap\",\"responses\":{\"200\":{\"description\":\"default response\",\"content\":{\"*/*\":{\"schema\":{\"$ref\":\"#/components/schemas/ImmutableMultimapStringString\"}}}}}}}},\"components\":{\"schemas\":{\"ImmutableMultimapStringString\":{\"type\":\"object\",\"properties\":{\"empty\":{\"type\":\"boolean\"}}}}}}"
Expected behavior
org.springdoc.webmvc.api.OpenApiResource#openapiJson
should not do the conversion fromOpenAPI
toString
. Just returnOpenAPI
orResponseEntity<OpenAPI>
and leave the serialization for the registeredHttpMessageConverter
.Additional context
An ugly fix for this problem is https://github.com/Geneea/springdoc-test/blob/master/src/main/java/com/geneea/springdoc/FixApiDocsWithFilter.java
registering a
Filter
what corrects the malformed response.Same problem is described in https://stackoverflow.com/questions/59393191/endpoint-api-docs-doesnt-work-with-custom-gsonhttpmessageconverter
The text was updated successfully, but these errors were encountered: