Search code examples
spring-bootopenapispringdocspringdoc-openapi-ui

How to annotate to require individual elements of requestBody in springdoc?


I have a spring (boot) server and want to generate the OpenAPI spec from the annotations with springdoc.

I have a request with two parameters in the request body. I want the first to be required and the second to be optional.

@RequestBody(required = {true|false}) seems to only set all parameters in the body to (not) required. The Javadoc for @Parameter on the other hand say to use io.swagger.v3.oas.annotations.parameters.RequestBody

This is my code that I would expect to generate a spec where the first parameter is required and the second one is optional:

@GetMapping("/fstVector")
public ResponseEntity<Vector> fstV(@RequestBody final Vector v1, @RequestBody(required = false) final Vector v2) {
    return new ResponseEntity<>(v1, HttpStatus.OK);
}

@PostMapping("/fstVector")
public ResponseEntity<Vector> fstVPost(@RequestBody(required = true) final Vector v1, @RequestBody(required = false) final Vector v2) {
    return new ResponseEntity<>(v1, HttpStatus.OK);
}

The generated spec however has both parameters required:

  /pond/fstVector:
    get:
      tags:
      - circle-escape-controller
      operationId: fstV
      parameters:
      - name: v1
        in: query
        required: true
        schema:
          $ref: '#/components/schemas/Vector'
      - name: v2
        in: query
        required: true
        schema:
          $ref: '#/components/schemas/Vector'
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Vector'
    post:
      tags:
      - circle-escape-controller
      operationId: fstVPost
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                v1:
                  $ref: '#/components/schemas/Vector'
                v2:
                  $ref: '#/components/schemas/Vector'
        required: true
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Vector'

How can I require only a specific parameter for all four request types?


Solution

  • Important

    • There should not be more than 1 Request Body to a given endpoint!
    • Request Body is mostly a JSON object. Thus to make some attribute in the body mandatory it's suggested to use the validation api.
    • There are 2 @RequestBody annotations. One from the Spring framework org.springframework.web.bind.annotation.RequestBody and another from io.swagger.v3.oas.annotations.parameters.RequestBody

    Importantly, even when you use the io.swagger.v3.oas.annotations.parameters.RequestBody from the Swagger library, you'll still need to use the org.springframework.web.bind.annotation.RequestBody to receive the actual object.

    Refactoring the code as below should be helpful in your case

    Controller Class

    @GetMapping("/fstVector")
    public ResponseEntity<Vector> fstV(
        // we generally use @RequestParam for query parameters. Query parameters are generally optional and thus the "required" attribute of @Parameter defaults to "false"
        @Parameter @RequestParam final Vector v1, 
        // set @Parameter to TRUE if the parameter must be passed.
        @Parameter(required = true) @RequestParam final Vector v2 
    ) {
        return new ResponseEntity<>(v1, HttpStatus.OK);
    }
        
    @PostMapping("/fstVector")
    public ResponseEntity<Vector> fstVPost(
        // RequestBody objects are "required" by default. To make them optional, add "(required = false)"
        @org.springframework.web.bind.annotation.RequestBody   // Spring
        @io.swagger.v3.oas.annotations.parameters.RequestBody  // Swagger
        @Valid // Bean validation to ensure if the incoming object is valid
        final Vector v1
    ) {
        return new ResponseEntity<>(v1, HttpStatus.OK);
    }
    

    For domain object, refactor the DTO as below

    DTO

    @Schema(description = "My DTO")
    class Vector {
       // The below attribute is required
       @NotNull
       @Parameter(description = "my first attribute", required = true)
       String attribute1;
    
       // The below attribute is optional
       @Parameter(description = "my second attribute", required  = false)
       String attribute2;
    }