bocodes
bocodes

Reputation: 447

What should I use in place of @ApiOperation(response) and @ApiResponse(response) when migrating from Springfox to Springdoc?

I am currently migrating from springfox to springdoc-openapi-ui and have run into some issues that the migration documentation does not touch on.

I am getting the error Cannot resolve method 'response' at response = Example.class and response = ErrorResponse.class below.

I've tried using responses instead but had no luck.

With springfox, my code looked like this:

@ApiOperation(
        value = "sample summary",
        response = Example.class,
        notes = "sample description")
@ApiResponses(value = {
        @ApiResponse(code = 200, message = "Successful"),
        @ApiResponse(code = 400, message = "Bad Request", response = ErrorResponse.class),
        @ApiResponse(code = 401, message = "Not Authorized", response = ErrorResponse.class),
        @ApiResponse(code = 403, message = "Forbidden", response = ErrorResponse.class),
        @ApiResponse(code = 404, message = "Not Found", response = ErrorResponse.class)})
@PostMapping(value = {"/sampleEndpoint"}, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)

after migrating to springdoc, here's where it stands:

@Operation(
        summary = "sample summary",
        response = Example.class,
        description = "sample description")
@ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "Successful"),
        @ApiResponse(responseCode = "400", description = "Bad Request", response = ErrorResponse.class),
        @ApiResponse(responseCode = "401", description = "Not Authorized", response = ErrorResponse.class),
        @ApiResponse(responseCode = "403", description = "Forbidden", response = ErrorResponse.class),
        @ApiResponse(responseCode = "404", description = "Not Found", response = ErrorResponse.class)})
@PostMapping(value = {"/sampleEndpoint"}, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)

Upvotes: 3

Views: 2673

Answers (1)

R4N
R4N

Reputation: 2595

You can use

content = @Content(mediaType =...,schema = @Schema(implementation=...))

You didn't show the rest of your code, but I'm assuming you're returning your ErrorResponse class as json in the 4xx responses?

If so, I think the spring-doc definition would look something like this:

@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ErrorResponse.class)))

I couldn't find a direct example of this on the base spring-doc page, but how to hide a schema has something similar: https://springdoc.org/#how-can-i-hide-schema-of-the-the-response

Edit for your comments:

Since you specified the produces type in the PostMapping spring-doc will infer this for all responses by default, so there's no need to specify that in the @Content annotation.

It's not entirely clear to me what your method signature looks like:

  1. Are you returning ResponseEntity<String> and mapping the json in the error response yourself?
  2. Or are you returning ResponseEntity<ErrorResponse> and then returning an empty body on 200?

spring-doc will automatically attempt to infer the response schema based on the return type, so if you're doing the second one, you won't need to specify the @Content type at all (except on the 200 response where you're returning an empty body, where you'll want to specify just by using an empty @Content annotation to omit the schema).

Example of #1

If you'd like the schema (with examples) to show up in your spring-doc interface you're going to want to include the schema = @Schema(implementation = ...) portion.

Here's a further example of what I think you might be trying to achieve:

    @Operation(summary = "sample summary",
            description = "sample description")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successful", content = @Content),
            @ApiResponse(responseCode = "400", description = "Bad Request", content =
            @Content(schema = @Schema(implementation = ErrorResponse.class))),
            @ApiResponse(responseCode = "401", description = "Not Authorized", content =
            @Content(schema = @Schema(implementation = ErrorResponse.class))),
            @ApiResponse(responseCode = "403", description = "Forbidden", content =
            @Content(schema = @Schema(implementation = ErrorResponse.class))),
            @ApiResponse(responseCode = "404", description = "Not Found", content =
            @Content(schema = @Schema(implementation = ErrorResponse.class)))
    })
    @PostMapping(value = {"/sampleEndpoint"}, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<String> postSampleEndpoint() {
        // just a fake response here
        ErrorResponse exampleErrorResponse = new ErrorResponse("fakeErrorDescription", "fakeSuggestion");
        ObjectMapper mapper = new ObjectMapper();
        String jsonErrorResult;
        try {
            jsonErrorResult = mapper.writeValueAsString(exampleErrorResponse);
        } catch (JsonProcessingException e) {
            // fill the json error manually with something else if mapping fails
            throw new RuntimeException(e);
        }
        return new ResponseEntity<>(jsonErrorResult, HttpStatus.BAD_REQUEST);
    }

Example of #2

    @Operation(summary = "sample summary",
            description = "sample description")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Successful", content = @Content),
            @ApiResponse(responseCode = "400", description = "Bad Request"),
            @ApiResponse(responseCode = "401", description = "Not Authorized"),
            @ApiResponse(responseCode = "403", description = "Forbidden"),
            @ApiResponse(responseCode = "404", description = "Not Found")
    })
    @PostMapping(value = {"/sampleEndpoint"}, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<ErrorResponse> postSampleEndpoint() {
        // just a fake response here
        ErrorResponse exampleErrorResponse = new ErrorResponse("fakeErrorDescription", "fakeSuggestion");
        return new ResponseEntity<>(exampleErrorResponse, HttpStatus.BAD_REQUEST);
    }

Then in the ErrorResponse model class you can use the @Schema annotation to provide examples:

ErrorResponse.java:

public class ErrorResponse {
    @JsonProperty("error_description")
    @Schema(example = "Example Error Description")
    private String errorDescription;

    @Schema(example = "Example Suggestion")
    private String suggestion;

    public ErrorResponse(String errorDescription, String suggestion) {
        this.errorDescription = errorDescription;
        this.suggestion = suggestion;
    }

    public String getErrorDescription() {
        return errorDescription;
    }

    public void setErrorDescription(String errorDescription) {
        this.errorDescription = errorDescription;
    }

    public String getSuggestion() {
        return suggestion;
    }

    public void setSuggestion(String suggestion) {
        this.suggestion = suggestion;
    }
}

Which generates a swagger doc UI like this:

Spring_Doc_Swagger_Example

Upvotes: 5

Related Questions