Skip to content

application/problem+json content type is not set for ProblemDetails #2982

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
T-bond opened this issue May 6, 2025 · 1 comment
Open

application/problem+json content type is not set for ProblemDetails #2982

T-bond opened this issue May 6, 2025 · 1 comment

Comments

@T-bond
Copy link

T-bond commented May 6, 2025

Thank you for implementing #2963, but it seems to be it is not working yet.

Describe the bug
application/problem+json content type should be generated for endpoints, where the content type is not explicitly overridden,
but it still generates the default produces media type.

To Reproduce
Has the same setup as described in the referenced ticket, but with Springdoc 2.8.7 (or preferably 2.8.8)
Spring configuration:

spring.mvc.problemdetails.enabled=true
springdoc.default-produces-media-type=application/json
...
import org.springframework.http.ProblemDetail

@ControllerAdvice
class GlobalExceptionHandler {
    @ExceptionHandler
    @ApiResponse(responseCode = "404", description = "Description for API users...")
    fun myCustomError(exception: MyCustomException) = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST)
}

(I replaced ResponseStatus with ApiResponse just to provide a description to the users of the API, but from the code it seems like (from commit 0cd571a), the ApiResponse annotation also required for the automated ProblemDetail content type resolver to work)

  • Investigate that the generated content type is still the default media type, not the application/problem+json.
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'

Expected behavior

  • The RFC defined problem detail content type is generated in the OpenAPI documentation.

Screenshots
Seems like the check for replacing the content type only checks for the configuration default content type */*, instead of
the springdoc.default-produces-media-type configured one.
Image

Additional context

Possible solution:
I have not tested it, but if I get this right, instead of using MediaType.ALL_VALUE in line:

io.swagger.v3.oas.models.media.MediaType mediaType = content.get(MediaType.ALL_VALUE);
and
it should use: springDocConfigProperties.getDefaultProducesMediaType() .

Output of `./gradlew -q :app:dependencyInsight --dependency org.springdoc`:

org.springdoc:springdoc-openapi-starter-common:2.8.8
  Variant compile:
    | Attribute Name                     | Provided | Requested    |
    |------------------------------------|----------|--------------|
    | org.gradle.status                  | release  |              |
    | org.gradle.category                | library  | library      |
    | org.gradle.libraryelements         | jar      | classes      |
    | org.gradle.usage                   | java-api | java-api     |
    | org.gradle.dependency.bundling     |          | external     |
    | org.gradle.jvm.environment         |          | standard-jvm |
    | org.gradle.jvm.version             |          | 21           |
    | org.jetbrains.kotlin.platform.type |          | jvm          |

org.springdoc:springdoc-openapi-starter-common:2.8.8
\--- org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.8
     \--- org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8
          \--- compileClasspath

org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.8
  Variant compile:
    | Attribute Name                     | Provided | Requested    |
    |------------------------------------|----------|--------------|
    | org.gradle.status                  | release  |              |
    | org.gradle.category                | library  | library      |
    | org.gradle.libraryelements         | jar      | classes      |
    | org.gradle.usage                   | java-api | java-api     |
    | org.gradle.dependency.bundling     |          | external     |
    | org.gradle.jvm.environment         |          | standard-jvm |
    | org.gradle.jvm.version             |          | 21           |
    | org.jetbrains.kotlin.platform.type |          | jvm          |

org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.8
\--- org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8
     \--- compileClasspath

org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8 (selected by rule)
  Variant compile:
    | Attribute Name                     | Provided | Requested    |
    |------------------------------------|----------|--------------|
    | org.gradle.status                  | release  |              |
    | org.gradle.category                | library  | library      |
    | org.gradle.libraryelements         | jar      | classes      |
    | org.gradle.usage                   | java-api | java-api     |
    | org.gradle.dependency.bundling     |          | external     |
    | org.gradle.jvm.environment         |          | standard-jvm |
    | org.gradle.jvm.version             |          | 21           |
    | org.jetbrains.kotlin.platform.type |          | jvm          |

org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8
\--- compileClasspath

A web-based, searchable dependency report is available by adding the --scan option.

@marceloverdijk
Copy link

I came here via #2963 as I'm facing the same issue.

I also have springdoc.default-produces-media-type=application/json, but I want to use application/problem+json for error responses (400, 401, etc).

Ideally springdoc should be able to use the produces from the ExceptionHandler like:

@ExceptionHandler(value = [Throwable::class], produces = [APPLICATION_PROBLEM_JSON_VALUE])

but that seems not to be supported at the moment.

Note I got it working by adding the @ApiResponse explicitly, but it's a bit tedious and contains a lot of duplication to that for all exception handlers.
Also note I'm using a custom ProblemDetailDto which contains @Schema descriptions for better documentation..

@RestControllerAdvice
class GlobalExceptionHandler : ResponseEntityExceptionHandler() {
    companion object {
        private val BLANK_TYPE: URI = URI.create("about:blank")
    }

    @ExceptionHandler(value = [Throwable::class], produces = [APPLICATION_PROBLEM_JSON_VALUE])
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    @ApiResponse(
        responseCode = "500",
        content = [
            Content(
                mediaType = APPLICATION_PROBLEM_JSON_VALUE,
                schema = Schema(implementation = ProblemDetailDto::class),
            ),
        ],
    )
    fun error(e: Throwable): ResponseEntity<ProblemDetailDto> {
        val status = HttpStatus.INTERNAL_SERVER_ERROR
        val problemDetail =
            ProblemDetailDto(
                type = BLANK_TYPE,
                status = status.value(),
                title = status.reasonPhrase,
            )
        return ResponseEntity
            .status(status)
            .contentType(APPLICATION_PROBLEM_JSON)
            .body(problemDetail)
    }
}
Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants