swagger-api / swagger-codegen

swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
http://swagger.io
Apache License 2.0
17.04k stars 6.03k forks source link

[java spring] Doesn't support different response models #4693

Open stevecookform3 opened 7 years ago

stevecookform3 commented 7 years ago
Description

If you have a schema that has multiple responses with different models, the generated controller code only supports the first model.

eg. using the simple pet example (and changing the 'default' response to '400'):

      responses:
        '200':
          description: pet response
          schema:
            type: array
            items:
              $ref: '#/definitions/pet'
        '400':
          description: unexpected error
          schema:
            $ref: '#/definitions/errorModel'

Generates the following interface:

@ApiOperation(value = "", notes = "Returns all pets from the system that the user has access to", response = Pet.class, responseContainer = "List", tags={  })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = "pet response", response = Pet.class),
        @ApiResponse(code = 400, message = "unexpected error", response = Pet.class) })
    @RequestMapping(value = "/pets",
        produces = { "application/json", "application/xml", "text/xml", "text/html" }, 
        consumes = { "application/json" },
        method = RequestMethod.GET)
    ResponseEntity<List<Pet>> findPets(@ApiParam(value = "tags to filter by") @RequestParam(value = "tags", required = false) List<String> tags,
        @ApiParam(value = "maximum number of results to return") @RequestParam(value = "limit", required = false) Integer limit);

Note that the error model isnt referenced anywhere in the generated code, and the response is set to ResponseEntity<List<Pet>> rather than the more generic Reponse class.

Swagger-codegen version

Latest head. Also the current version on http://editor.swagger.io/#/

Command line used for generation

generate -v -i ./swagger.yaml -l spring -o .

jfiala commented 7 years ago

This is a bug in all Java languages, I created #4718 for this to fix it in every languages. The fix is to use the baseType instead of the returnType for the response. Additionally, you also need a fix for checking for void baseTypes which I added in PR #4717 to AbstractJavaJAXRSServerCodegen.java.

jfiala commented 7 years ago

So it is best to wait for #4717 to get merged, then we can fix it in Spring and in all other languages.

paranoid945 commented 7 years ago

any updates?

ePaul commented 7 years ago

@jfiala Just for understanding, you want to change the return type to be just Response instead of Response<List<Pet>> in this case? I'm not sure I approve raw type usage, though I suppose Java doesn't allow anything type-safe here. (Apart from throwing all the non-first responses as exceptions.)

JLLeitschuh commented 7 years ago

The alternative here is to require uses to implement a controller advice class that correctly maps exceptions thrown in the controllers to the correct object.

https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

The generation of the @ApiResponse annotations should be fixed though.

@ApiResponses(value = { 
        @ApiResponse(code = 200, message = "pet response", response = Pet.class),
        @ApiResponse(code = 400, message = "unexpected error", response = Pet.class) }
kkomissarchik commented 6 years ago

I just tried version 2.3.1 of the codegen and this issue is still there. Is there an ETA for fixing? If not, is there an alternative swagger pattern for error responses that does generate api classes in spring that can be used directly without making fixes by hand?

msilvestre commented 4 years ago

Hi, I'm also struggling with this issue. What is the work around?

My generated ..Api.java has the response well defined.

    @ApiResponses(value = { 
        @ApiResponse(code = 201, message = "Created", response = TheObject.class),
        @ApiResponse(code = 400, message = "Bad Request", response = BadRequest.class) })
    @RequestMapping(value = "/v1/objects",
        produces = { "application/json" }, 
        consumes = { "application/json" },
        method = RequestMethod.POST)
    ResponseEntity<TheObject> postObjects(@ApiParam(value = ""  )  @Valid @RequestBody TheObject body

Is there any work around? I have changed the return to:

ResponseEntity<? extends java.lang.Object> postObjects(@ApiParam(value = ""  )  @Valid @RequestBody TheObject body

Which is hard to check on git any changes made to the openapi spec.

Thanks!

cmasdf commented 3 years ago

Push - looks like this problem is still present. If not i would appreciate someone giving a proper implementation advice.

@wing328 could you give us an update please?

max-poprawe commented 3 years ago

Hi all - we are running into exactly the same issue. Unfortunately, the proposal from @JLLeitschuh also does not work as stated (afaik), as the method would need to throw an Exception (to be handled by the Controller's @ExceptionHandler) which it is also not allowed to (the signature in the ...API class does not specify a "throws").

Possible solutions: 1) The proposal from @msilvestre to make the function return ResponseEntity<? extends java.lang.Object>. As him, I don't think this is very nice. 2) add a generic throws Exception to the method signature. This allows the implementation in the controller to throw Exceptions of any kind and we can handle and transform them into proper responses with @ExceptionHandlers --> Also not very nice 3) "Full blown": Auto generate Exceptions for the different return paths (all 4xx and 5xx?), allow the method to throw them, autogenerate @ExceptionHandlers for these Exceptions on the generated Controller.

I envision option 3 for the example above from @msilvestre to look something like this (obviously distributed across adequate files):

@ApiResponses(value = { 
        @ApiResponse(code = 201, message = "Created", response = TheObject.class),
        @ApiResponse(code = 400, message = "Bad Request", response = BadRequest.class) })
    @RequestMapping(value = "/v1/objects",
        produces = { "application/json" }, 
        consumes = { "application/json" },
        method = RequestMethod.POST)
    ResponseEntity<TheObject> postObjects(@ApiParam(value = ""  )  @Valid @RequestBody TheObject body throws BadRequestException

public class BadRequestException extends Exception {}

@Controller
class ........ {

    @ResponseStatus(code=HttpStatus.BAD_REQUEST, reason="Bad Request")
    @ExceptionHandler(BadRequestException.class)
    public BadRequest handleBadRequest () { // Note that this returns the BadRequest class
      return null;
    }

}

I am reasonably new to all of this, so I am not sure I have the full picture. I am happy to contribute to a solution if there is some vetting on my thinking and you guys agree / lead me to the right direction.

juandanielmorcilloregueiro commented 3 years ago

Hi,

I´m facing the same issue. Is there any fix?

Thanks.

abaveja313 commented 2 years ago

I am facing this error too

DG0lden commented 1 year ago

Any chance for fix?