Skip to content

Custom Requestmapping consumes responses #1546

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

Closed
T-Developer1 opened this issue Mar 11, 2022 · 1 comment
Closed

Custom Requestmapping consumes responses #1546

T-Developer1 opened this issue Mar 11, 2022 · 1 comment
Labels
bug Something isn't working

Comments

@T-Developer1
Copy link

T-Developer1 commented Mar 11, 2022

What i am trying to do

I am trying to set a specific consumes / produces Type for multiple Mappings.

I know that i can set them like this:

@PostMapping( "/testPostMapping", consumes = { MediaType.APPLICATION_CBOR_VALUE } )
but then i have to add consumes and produces to every Mapping method.

I have an Controller Class which sets produces for all my RequestMappings

@RequestMapping( produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE } )
public abstract class Controller
{

}

I extend this Controller and have some Mappings inside it.

public class ExampleController extends Controller
{

   @GetMapping( value = "/getTest" )
   public @ResponseBody ResponseEntity<String> testGetMapping()
   {
      return new ResponseEntity<String>( "TestString", HttpStatus.OK );
   }

   @PostMapping( "/testPostMapping" )
   public @ResponseBody ResponseEntity<String> testPostMapping( String testString )
   {
      return new ResponseEntity<String>( "TestString", HttpStatus.OK );

   }

}

Now i only want to set the consumes Type to the PostMapping.

What i already tried

I already tried to add The @ApiResponse Annotation

@ApiResponses( value = {
                         @ApiResponse( responseCode = "200", content = @Content( mediaType = MediaType.APPLICATION_XML_VALUE ) ) } )

public @interface DefaultProduces
{
   String[] produces() default {

                                 MediaType.APPLICATION_XML_VALUE };
}

but this will override the content Type of my Requests

if i add this to two @PostMappings , and one Returns a String the other one Returns an HTML element
@ApiResponse will override the schema type information

Without the ApiResponse Annotation

           "content": {
              "*/*": {
                "schema": {
                  "type": "string"
                }
              }

With the ApiResponse Annotation

"content": {
              "application/xml": {
                
              }

How i attampted it

I made my own Annotations

@Target( { ElementType.TYPE, ElementType.METHOD } )
@Retention( RetentionPolicy.RUNTIME )
@Inherited
public @interface DefaultProduces
{
   String[] produces() default {

                                 MediaType.TEXT_HTML_VALUE };
}

and extended the WebMvcConfig RequestMappingHandler

   @Override
   protected RequestMappingHandlerMapping createRequestMappingHandlerMapping()
   {
      return new ExtendedRequestMappingHandlerMapping();
   }

So that if i add @DefaultProduces to a RequestMapping it will have the MediaTypes which i added there

I overrode the RequestMappingInfo with my customized infos.

But OpenAPI does not take the Informations from RequestMappingInfo .

Describe the solution you'd like

Is there a way to let openAPI get his Informations from the RequestMappingInfo.

Or is there an Annotation like Request @ApiResponse where i can only set the consumes or produces Types without chaning anything else.

** TestProject **

In my TestProject is a Controller with a PostMapping like this

   @DefaultConsumes
   @PostMapping( value = "/testPostMapping" )
   public @ResponseBody ResponseEntity<String> testPostMapping( String testString )
   {
      return new ResponseEntity<String>( "TestString", HttpStatus.OK );
   }

My annotation DefaultConsomes makes the PostMapping only accapt text/plain

The OpenAPI Json still says that the content is /

 "content": {
              "*/*": {
                "schema": {
                  "type": "string"
                }
              }

which leads to the following Curl request in Swagger-ui

`curl -X 'POST' \
  'http://localhost:8080/testPostMapping' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '"string"'`

The PostMapping will response with

{
  "timestamp": 1647005196793,
  "status": 406,
  "error": "Not Acceptable",
  "path": "/testPostMapping"
}

If text/plain is set it is working.

curl -o - -X 'POST' \
  'http://localhost:8080/testPostMapping' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '"string"'

should output "TestString"

Here is a basic Project with my issue

https://github.com/T-Developer1/SpringdocCustomAnnotationSample

Additional Informations

In OpenApiResources the consumes and Produces are correct

String[] produces = requestMappingInfo.getProducesCondition().getProducibleMediaTypes().stream().map(MimeType::toString).toArray(String[]::new);
String[] consumes = requestMappingInfo.getConsumesCondition().getConsumableMediaTypes().stream().map(MimeType::toString).toArray(String[]::new);

but the Informations get lost after that
In AbstractOpenApiResources produces and consumes are null

String[] methodConsumes = routerOperation.getConsumes();
String[] methodProduces = routerOperation.getProduces();

And even if you change the value there, it will be overriden again in MethodAttribute

PostMapping reqPostMappingMethod = AnnotatedElementUtils.findMergedAnnotation(method, PostMapping.class);
if (reqPostMappingMethod != null) {
fillMethods(reqPostMappingMethod.produces(), reqPostMappingMethod.consumes(), reqPostMappingMethod.headers());
return;
}

@bnasslahsen
Copy link
Collaborator

@T-Developer1,

I have added a fix to handle this case out of the box.
Feel free to test with the latest SNAPSHOT and provide your feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants