【问题标题】:Is it possible to generate controller which returns void but not Void (object type) using Open API code generator in Java?是否可以使用 Java 中的 Open API 代码生成器生成返回 void 但不返回 Void(对象类型)的控制器?
【发布时间】:2022-02-14 17:47:28
【问题描述】:

我使用开放 api 生成器(gradle 的实现)在 Java 中为我的 API 生成控制器,但如果我的端点什么也不返回 - OpenAPI 生成器生成返回类型为对象类型 Void,但不是 void。

我希望:

public void createPet() {}

但是得到了:

public Void createPet() { return null; }

我尝试自定义胡子模板,但没有任何选项。 gradle 任务配置中的 ConfigOptions 也没有相应的选项。

当前 .mustache api 模板:

/**
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package {{package}};

{{#imports}}import {{import}};
{{/imports}}
{{#swagger2AnnotationLibrary}}
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.Parameter;
    import io.swagger.v3.oas.annotations.Parameters;
    import io.swagger.v3.oas.annotations.media.Content;
    import io.swagger.v3.oas.annotations.media.Schema;
    import io.swagger.v3.oas.annotations.responses.ApiResponse;
    import io.swagger.v3.oas.annotations.security.SecurityRequirement;
    import io.swagger.v3.oas.annotations.tags.Tag;
{{/swagger2AnnotationLibrary}}
{{#swagger1AnnotationLibrary}}
    import io.swagger.annotations.*;
{{/swagger1AnnotationLibrary}}
{{#jdk8-no-delegate}}
    {{#virtualService}}
        import io.virtualan.annotation.ApiVirtual;
        import io.virtualan.annotation.VirtualService;
    {{/virtualService}}
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
{{/jdk8-no-delegate}}
{{#useBeanValidation}}
    import org.springframework.validation.annotation.Validated;
{{/useBeanValidation}}
{{#useSpringController}}
    import org.springframework.stereotype.Controller;
{{/useSpringController}}
import org.springframework.web.bind.annotation.*;
{{#jdk8-no-delegate}}
    {{^reactive}}
        import org.springframework.web.context.request.NativeWebRequest;
    {{/reactive}}
{{/jdk8-no-delegate}}
import org.springframework.web.multipart.MultipartFile;
{{#reactive}}
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    import org.springframework.http.codec.multipart.Part;
{{/reactive}}

{{#useBeanValidation}}
    import javax.validation.Valid;
    import javax.validation.constraints.*;
{{/useBeanValidation}}
import java.util.List;
import java.util.Map;
{{#jdk8-no-delegate}}
    import java.util.Optional;
{{/jdk8-no-delegate}}
{{^jdk8-no-delegate}}
    {{#useOptional}}
        import java.util.Optional;
    {{/useOptional}}
{{/jdk8-no-delegate}}
{{#async}}
    import java.util.concurrent.{{^jdk8}}Callable{{/jdk8}}{{#jdk8}}CompletableFuture{{/jdk8}};
{{/async}}
import javax.annotation.Generated;

{{>generatedAnnotation}}
{{#useBeanValidation}}
    @Validated
{{/useBeanValidation}}
{{#useSpringController}}
    @Controller
{{/useSpringController}}
{{#swagger2AnnotationLibrary}}
    @Tag(name = "{{{baseName}}}", description = "the {{{baseName}}} API")
{{/swagger2AnnotationLibrary}}
{{#swagger1AnnotationLibrary}}
    @Api(value = "{{{baseName}}}", description = "the {{{baseName}}} API")
{{/swagger1AnnotationLibrary}}
{{#operations}}
    {{#virtualService}}
        @VirtualService
    {{/virtualService}}
    public interface {{classname}} {
    {{#jdk8-default-interface}}
        {{^isDelegate}}
            {{^reactive}}

                default Optional<NativeWebRequest> getRequest() {
                    return Optional.empty();
                    }
            {{/reactive}}
        {{/isDelegate}}
        {{#isDelegate}}

                default {{classname}}Delegate getDelegate() {
                return new {{classname}}Delegate() {};
                }
        {{/isDelegate}}
    {{/jdk8-default-interface}}
    {{#operation}}

            /**
            * {{httpMethod}} {{{path}}}{{#summary}} : {{.}}{{/summary}}
        {{#notes}}
                * {{.}}
        {{/notes}}
            *
        {{#allParams}}
                * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
        {{/allParams}}
            * @return {{#responses}}{{message}} (status code {{code}}){{^-last}}
                *         or {{/-last}}{{/responses}}
        {{#isDeprecated}}
                * @deprecated
        {{/isDeprecated}}
        {{#externalDocs}}
                * {{description}}
                * @see <a href="{{url}}">{{summary}} Documentation</a>
        {{/externalDocs}}
            */
        {{#virtualService}}
                @ApiVirtual
        {{/virtualService}}
        {{#swagger2AnnotationLibrary}}
                @Operation(
                operationId = "{{{operationId}}}",
            {{#summary}}
                    summary = "{{{.}}}",
            {{/summary}}
            {{#vendorExtensions.x-tags}}
                    tags = { {{#vendorExtensions.x-tags}}"{{tag}}"{{^-last}}, {{/-last}}{{/vendorExtensions.x-tags}} },
            {{/vendorExtensions.x-tags}}
                responses = {
            {{#responses}}
                    @ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}"{{#baseType}}, content = @Content(mediaType = "application/json", schema = @Schema(implementation =  {{{baseType}}}.class)){{/baseType}}){{^-last}},{{/-last}}
            {{/responses}}
                }{{#hasAuthMethods}},
                security = {
            {{#authMethods}}
                    @SecurityRequirement(name = "{{name}}"{{#isOAuth}}, scopes={ {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} }{{/isOAuth}}){{^-last}},{{/-last}}
            {{/authMethods}}
                }{{/hasAuthMethods}}
                )
        {{/swagger2AnnotationLibrary}}
        {{#swagger1AnnotationLibrary}}
                @ApiOperation(
                tags = { {{#vendorExtensions.x-tags}}"{{tag}}"{{^-last}}, {{/-last}}{{/vendorExtensions.x-tags}} },
                value = "{{{summary}}}",
                nickname = "{{{operationId}}}",
                notes = "{{{notes}}}"{{#returnBaseType}},
                response = {{{.}}}.class{{/returnBaseType}}{{#returnContainer}},
                responseContainer = "{{{.}}}"{{/returnContainer}}{{#hasAuthMethods}},
                authorizations = {
            {{#authMethods}}
                {{#isOAuth}}
                        @Authorization(value = "{{name}}", scopes = {
                    {{#scopes}}
                            @AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}},{{/-last}}
                    {{/scopes}}
                        }){{^-last}},{{/-last}}
                {{/isOAuth}}
                {{^isOAuth}}
                        @Authorization(value = "{{name}}"){{^-last}},{{/-last}}
                {{/isOAuth}}
            {{/authMethods}} }{{/hasAuthMethods}}
                )
                @ApiResponses({
            {{#responses}}
                    @ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}.class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}
            {{/responses}}
                })
        {{/swagger1AnnotationLibrary}}
        {{#implicitHeaders}}
            {{#swagger2AnnotationLibrary}}
                    @Parameters({
                {{#headerParams}}
                    {{>paramDoc}}{{^-last}},{{/-last}}
                {{/headerParams}}
            {{/swagger2AnnotationLibrary}}
            {{#swagger1AnnotationLibrary}}
                    @ApiImplicitParams({
                {{#headerParams}}
                    {{>implicitHeader}}{{^-last}},{{/-last}}
                {{/headerParams}}
            {{/swagger1AnnotationLibrary}}
                })
        {{/implicitHeaders}}
            @RequestMapping(
            method = RequestMethod.{{httpMethod}},
            value = "{{{path}}}"{{#singleContentTypes}}{{#hasProduces}},
            produces = "{{{vendorExtensions.x-accepts}}}"{{/hasProduces}}{{#hasConsumes}},
            consumes = "{{{vendorExtensions.x-contentType}}}"{{/hasConsumes}}{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}},
            produces = { {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} }{{/hasProduces}}{{#hasConsumes}},
            consumes = { {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} }{{/hasConsumes}}{{/singleContentTypes}}
            )
        {{#jdk8-default-interface}}default {{/jdk8-default-interface}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}{{>returnTypes}}{{#responseWrapper}}>{{/responseWrapper}} {{#delegate-method}}_{{/delegate-method}}{{operationId}}(
        {{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{>cookieParams}}{{^-last}},
        {{/-last}}{{/allParams}}{{#reactive}}{{#hasParams}},
        {{/hasParams}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true){{/swagger2AnnotationLibrary}}{{#springFoxDocumentationProvider}}@ApiIgnore{{/springFoxDocumentationProvider}} final ServerWebExchange exchange{{/reactive}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}},
        {{/hasParams}}{{#springFoxDocumentationProvider}}@ApiIgnore {{/springFoxDocumentationProvider}}{{#springDocDocumentationProvider}}@ParameterObject {{/springDocDocumentationProvider}}final Pageable pageable{{/vendorExtensions.x-spring-paginated}}
            ){{^jdk8-default-interface}};{{/jdk8-default-interface}}{{#jdk8-default-interface}}{{#unhandledException}} throws Exception{{/unhandledException}} {
        {{#delegate-method}}
                return {{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#reactive}}{{#hasParams}}, {{/hasParams}}exchange{{/reactive}}{{#vendorExtensions.x-spring-paginated}}, pageable{{/vendorExtensions.x-spring-paginated}});
                }

                // Override this method
            {{#jdk8-default-interface}}default {{/jdk8-default-interface}} {{#responseWrapper}}{{.}}<{{/responseWrapper}}{{>returnTypes}}{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}({{#allParams}}{{^isFile}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{{dataType}}}{{/reactive}}{{#reactive}}{{^isArray}}Mono<{{{dataType}}}>{{/isArray}}{{#isArray}}Flux<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{/isFile}}{{#isFile}}{{#reactive}}Flux<Part>{{/reactive}}{{^reactive}}MultipartFile{{/reactive}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#reactive}}{{#hasParams}}, {{/hasParams}}{{#springFoxDocumentationProvider}}@ApiIgnore{{/springFoxDocumentationProvider}} final ServerWebExchange exchange{{/reactive}}{{#vendorExtensions.x-spring-paginated}}, {{#springFoxDocumentationProvider}}@ApiIgnore{{/springFoxDocumentationProvider}} final Pageable pageable{{/vendorExtensions.x-spring-paginated}}){{#unhandledException}} throws Exception{{/unhandledException}} {
        {{/delegate-method}}
        {{^isDelegate}}
            {{>methodBody}}
        {{/isDelegate}}
        {{#isDelegate}}
                return getDelegate().{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#reactive}}{{#hasParams}}, {{/hasParams}}exchange{{/reactive}}{{#vendorExtensions.x-spring-paginated}}, pageable{{/vendorExtensions.x-spring-paginated}});
        {{/isDelegate}}
            }{{/jdk8-default-interface}}

    {{/operation}}
        }
{{/operations}}

在 gradle.build 中生成任务示例

openApiGenerate {
    generatorName = "spring"
    inputSpec = "$projectDir/src/main/resources/swagger/api.yaml".toString()
    outputDir = "$buildDir/generated".toString()
    apiPackage = "org.example.api"
    modelPackage = "org.example.resource"
    templateDir = "$projectDir/src/main/resources/templates/".toString()

    configOptions = [
        java8: "false",
        serializableModel: "true",
        interfaceOnly: "true",
    ]
}


api.yaml

swagger: "2.0"
info:
  version: 1.0.0
  title: Swagger Petstore
  license:
    name: MIT
host: petstore.swagger.io
basePath: /v1
schemes:
  - http
consumes:
  - application/json
produces:
  - application/json
paths:
  /pets:
    get:
      summary: List all pets
      operationId: listPets
      tags:
        - pets
      parameters:
        - name: limit
          in: query
          description: How many items to return at one time (max 100)
          required: false
          type: integer
          format: int32
      responses:
        "200":
          description: A paged array of pets
          headers:
            x-next:
              type: string
              description: A link to the next page of responses
          schema:
            $ref: '#/definitions/Pets'
        default:
          description: unexpected error
          schema:
            $ref: '#/definitions/Error'
    post:
      summary: Create a pet
      operationId: createPets
      tags:
        - pets
      responses:
        200:
          description: Ok
  /pets/{petId}:
    get:
      summary: Info for a specific pet
      operationId: showPetById
      tags:
        - pets
      parameters:
        - name: petId
          in: path
          required: true
          description: The id of the pet to retrieve
          type: string
      responses:
        "200":
          description: Expected response to a valid request
          schema:
            $ref: '#/definitions/Pets'
        default:
          description: unexpected error
          schema:
            $ref: '#/definitions/Error'
definitions:
  Pet:
    type: "object"
    required:
      - id
      - name
    properties:
      id:
        type: integer
        format: int64
      name:
        type: string
      tag:
        type: string
  Pets:
    type: array
    items:
      $ref: '#/definitions/Pet'
  Error:
    type: "object"
    required:
      - code
      - message
    properties:
      code:
        type: integer
        format: int32
      message:
        type: string

【问题讨论】:

标签: java swagger openapi swagger-codegen openapi-generator


【解决方案1】:

简介

让我们将openapi-generator5.3.1 版本视为当前版本。

总结

强制生成器使用void返回类型而不是Void似乎不可行。

作为最后的手段,可以考虑创建一个生成器的分支并实现所需的行为。

详情

已请求的功能

GitHub 问题已请求该功能:Change return types for Spring openapi-generator-maven-plugin generated interfaces · Issue #6135 · OpenAPITools/openapi-generator

相关问题:java - Change return types for Spring openapi-generator-maven-plugin generated interfaces - Stack Overflow

源代码

spring 生成器 (generatorName: spring) 由 org.openapitools.codegen.languages.SpringCodegen 类表示。

Void 类型检测由SpringCodegen 类实现。

请看源码的相关部分:openapi-generator/SpringCodegen.java at v5.3.1 · OpenAPITools/openapi-generator:

private void doDataTypeAssignment(String returnType, DataTypeAssigner dataTypeAssigner) {
    final String rt = returnType;
    if (rt == null) {
        dataTypeAssigner.setReturnType("Void");
    } else if (rt.startsWith("List")) {

【讨论】:

    【解决方案2】:

    我找到了解决方法。我已经使用了“查找和替换任务”。

    1. 使用 openApiGenerate 任务生成 API
    2. 查找“Void”类型并将其替换为 void

    如何查找和替换:

    Gradle task replace string in .java file

    【讨论】:

      猜你喜欢
      • 2020-11-02
      • 1970-01-01
      • 1970-01-01
      • 2019-07-03
      • 2014-08-24
      • 2012-06-03
      • 2012-08-08
      • 1970-01-01
      相关资源
      最近更新 更多