swagger-api / swagger-api/swagger-codegen

[JavaSpring] API-level consumes should only affect relevant HTTP Methods

Open
#7,575 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Mustache
Stars
17.8k
Forks
6k
PR merge metrics
No merged PRs in 30d

Description

Description

If I include the produces/consumes declarations at the top of the yaml spec then they are global and apply to every endpoint in the API. This is why I want to include them there - it means I can define them once and that's it.

However, that means it also applies the consumes to GET and DELETE requests, which don't take a body. I just wrote my first API using swagger, and my colleague who is consuming it in the first implementation can't call the GET endpoints. He's consuming it from a .NET application and the .NET httpClient throws an exception if you try to set a Content-Type on a GET request! But without it, my API says not found because it doesn't have a compatible Content-Type header. It might be possible to work around with an ugly reflection hack, but we don't don't want to go there!

I know I can simply specify the consumes at the endpoint level where relevant, but then I repeat it.

Swagger-codegen version

2.3.0

Swagger declaration file content or url

Example spec

swagger: '2.0'
host: api.example.com
basePath: /
schemes:
  - https
consumes:
  - application/json
  - application/xml
produces:
  - application/json
  - application/xml
tags:
  - name: foo
paths:
  /foo:
    get:
      operationId: getFoo
      tags:
        - foo      
      responses:
        '200':
          description: OK
    post:
      operationId: postFoo
      tags:
        - foo      
      responses:
        '200':
          description: OK
    put:
      operationId: putFoo
      tags:
        - foo      
      responses:
        '200':
          description: OK
    delete:
      operationId: deleteFoo
      tags:
        - foo      
      responses:
        '200':
          description: OK       

Produces this Java code

@Api(value = "foo", description = "the foo API")
public interface FooApi {

    FooApiDelegate getDelegate();

    @ApiOperation(value = "", nickname = "deleteFoo", notes = "", tags={ "foo", })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = "OK") })
    @RequestMapping(value = "/foo",
        produces = { "application/json", "application/xml" }, 
        consumes = { "application/json", "application/xml" },
        method = RequestMethod.DELETE)
    default CompletableFuture<ResponseEntity<Void>> deleteFoo() {
        return getDelegate().deleteFoo();
    }


    @ApiOperation(value = "", nickname = "getFoo", notes = "", tags={ "foo", })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = "OK") })
    @RequestMapping(value = "/foo",
        produces = { "application/json", "application/xml" }, 
        consumes = { "application/json", "application/xml" },
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<Void>> getFoo() {
        return getDelegate().getFoo();
    }


    @ApiOperation(value = "", nickname = "postFoo", notes = "", tags={ "foo", })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = "OK") })
    @RequestMapping(value = "/foo",
        produces = { "application/json", "application/xml" }, 
        consumes = { "application/json", "application/xml" },
        method = RequestMethod.POST)
    default CompletableFuture<ResponseEntity<Void>> postFoo() {
        return getDelegate().postFoo();
    }


    @ApiOperation(value = "", nickname = "putFoo", notes = "", tags={ "foo", })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = "OK") })
    @RequestMapping(value = "/foo",
        produces = { "application/json", "application/xml" }, 
        consumes = { "application/json", "application/xml" },
        method = RequestMethod.PUT)
    default CompletableFuture<ResponseEntity<Void>> putFoo() {
        return getDelegate().putFoo();
    }

}
Command line used for generation

Using swagger-codegen-maven-plugin:2.3.0 and swagger codegen 2.3.0
Using options:

  • serializableModel: true
  • dateLibrary: java8
  • java8: true
  • library: spring-boot
  • async: true
  • delegatePattern: true
  • useOptional: true
Steps to reproduce

Generate the above spec for spring-boot

Suggest a fix/enhancement

I think there should be an option (at least, if not make it the default behaviour) to only generate a consumes in the @RequestMapping annotation for POST, PUT and PATCH and not for DELETE, GET, etc.

I am aware there is some discussion around if body is allowed in DELETE for example. The spec doesn't forbid it, but says it is undefined behaviour. My own experience is that, in addition to the GET issue in .NET) our API Gateway (WS02) strips out the body from DELETE, so I would argue they should be avoid. However, I guess it needs to be an option - should it be default to generate consumes for all (as today) or default to only do it for those allowed? In terms of defining what's !allowed" I propose using the MDN definitions (https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) - each method includes a table specifying "Request has body" as Yes/No.

I'd even be happy to try and do this, but wanted some feedback if it's generally considered a good idea or not first!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the spring-boot generator using the example Swagger declaration and generation options from the issue, then inspect the generated Java interface and its @RequestMapping annotations. Done means API-level consumes is not emitted for methods without request bodies, while the selected behavior for POST, PUT, PATCH, and any configurable exceptions is covered by generated output.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
api, backend
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.