3

Before I start: I reviewed the following links and none of them matched my question:

So, I have OpenAPI 3 definition for an endpoint that returns a given payload for a successful call (e.g. 200 OK). However, for unsuccessful calls (e.g. 408 CONFLICT), I want to return a completely different error payload.

You can see the definition below:

openapi: 3.0.1

info:
  title: Dogs
  description: API to add dogs into a database
  version: 0.0.1

paths:
  /dog:
    post:
      description: Saves a dog into the database

      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Dog"

      responses:
        201:
          description: A dog was added to the system
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Dog"

        400:
          description: This dog already exist in the system
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

components:
  schemas:
    Dog:
      type: object
      properties:
        name:
          type: string
          description: Name of the dog
          example: "barky"

    Error:
      type: object
      properties:
        code:
          type: string
          description: Machine-readable code of the error.
          example: "Already Exists"

        message:
          type: string
          description: A Human-readable error message.
          example: "Empty string is not a valid name for a dog"

From that, I'm generating a Spring code (currently using the Swagger editor). The method signature that is associated with the definition looks like that:

ResponseEntity<Dog> dogPost(@ApiParam(value = ""  )  @Valid @RequestBody Dog body);

As a result - I cannot return the Error object in this function unless throwing an exception and catching in later on - which I consider a bad practice since the action of invalid input is not an exception.

Is there a way to solve this nicely without throwing an exception and catching it outside of the function scope?

1 Answers1

1

There are quite a few ways to implement this kind of requirement with Spring Boot. One of them is using an @ExceptionHandler (See https://www.baeldung.com/exception-handling-for-rest-with-spring for more info), but that is indeed then handled outside of the controller itself. The advantage of that system is that you can implement the conversion of any Exception to an error object in 1 place instead of each controller method separately.

That said, if you do want to handle it in the controller itself, you can do:

@PostRequest("/dog")
public ResponseEntity<?> createDog(@Valid @RequestBody Dog dog) {
    if( !service.doesDogAlreadyExist(dog) ) {
      Dog dog = service.createDog(dog);
      return ResponseEntity.ok(dog).build();
    } else {
      return ResponseEntity.status(HttpStatus.CONFLICT)
                           .body(/* error object here */)
    }
}
Wim Deblauwe
  • 25,113
  • 20
  • 133
  • 211
  • On a side-note: The check if there is already a Dog present can be done in a custom validator as well. See the chapter on validation in my book https://www.infoq.com/minibooks/spring-boot-building-api-backend/ (NOTE: I am the author) – Wim Deblauwe Dec 02 '19 at 16:39
  • 1
    Thing is that when using OpenAPI-generator the method signature is clashing with the generated interface signature and do not implement it. Which gives a compilation error. – Tom Carmi Feb 13 '22 at 17:57