简体   繁体   English

是否可以使用 Java 中的 Open API 代码生成器生成返回 void 但不返回 Void(对象类型)的 controller?

[英]Is it possible to generate controller which returns void but not Void (object type) using Open API code generator in Java?

I use open api generator (gradle's implementation) to generate controllers for my API in Java, but if my endpoint return nothing - OpenAPI generator generates return type as object type Void, but not as void. I use open api generator (gradle's implementation) to generate controllers for my API in Java, but if my endpoint return nothing - OpenAPI generator generates return type as object type Void, but not as void.

I expect:我预计:

public void createPet() {}

But got:但得到:

public Void createPet() { return null; }

I tried to customize mustache template, but there was no any options for that.我尝试自定义胡子模板,但没有任何选项。 ConfigOptions in gradle task's configuration also doesn't have options for that. gradle 任务配置中的 ConfigOptions 也没有相应的选项。

Current.mustache api template: Current.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}}

generate task example in gradle.build在 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 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

Introduction介绍

Let's consider the 5.3.1 version of openapi-generator as the current version.让我们将5.3.1版本的openapi-generator视为当前版本。

Summary概括

It does not seem to be feasible to force the generator to use the void return type instead of Void .强制生成器使用void返回类型而不是Void似乎不可行。

As a last resort, it may be considered to create a fork of the generator and implement the desired behavior.作为最后的手段,可以考虑创建生成器的分支并实现所需的行为。

Details细节

Already requested feature已请求的功能

The feature is already requested by the GitHub issue: Change return types for Spring openapi-generator-maven-plugin generated interfaces · Issue #6135 · OpenAPITools/openapi-generator . GitHub 问题已请求该功能: 更改 Spring openapi-generator-maven-plugin 生成的接口的返回类型 · 问题 #6135 · OpenAPITools/openapi-generator

The related question: java - Change return types for Spring openapi-generator-maven-plugin generated interfaces - Stack Overflow .相关问题: java - 更改 Spring openapi-generator-maven-plugin 生成的接口的返回类型 - 堆栈内存溢出

Source code源代码

The spring generator ( generatorName : spring ) is represented by the org.openapitools.codegen.languages.SpringCodegen class. spring生成器( generatorName : spring )由org.openapitools.codegen.languages.SpringCodegen class 表示。

The Void type detection is implemented by the SpringCodegen class. Void类型检测由SpringCodegen class 实现。

Please, see the related part of the source code: openapi-generator/SpringCodegen.java at v5.3.1 · OpenAPITools/openapi-generator :请参阅源代码的相关部分: 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")) {

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM