Duplicated body fields with @BeanParam and JSON body

892 views
Skip to first unread message

Théo A. Monteiro

unread,
Apr 6, 2017, 9:06:28 AM4/6/17
to Swagger
Hi,

Swagger is creating two body fields when I try to use a @BeanParam and a JSON body.


I tried to use swagger-jaxrs and swagger-jersey2-jaxrs [1][2], but both result the same problem. Is there any way to circumvent this?

[1]. https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-JAX-RS-Project-Setup-1.5.X
[2]. https://github.com/swagger-api/swagger-core/issues/1720

This is a PoC that shows the problem:

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   
<modelVersion>4.0.0</modelVersion>
   
<groupId>org.example</groupId>
   
<artifactId>unrested</artifactId>
   
<version>0.0.1-SNAPSHOT</version>
   
<packaging>war</packaging>

   
<properties>
       
<version.jboss.bom.eap>7.0.4.GA</version.jboss.bom.eap>
       
<version.wildfly.maven.plugin>1.0.2.Final</version.wildfly.maven.plugin>
   
</properties>

   
<build>
       
<pluginManagement>
           
<plugins>
               
<plugin>
                   
<groupId>org.wildfly.plugins</groupId>
                   
<artifactId>wildfly-maven-plugin</artifactId>
                   
<version>${version.wildfly.maven.plugin}</version>
                   
<configuration>
                       
<port>9994</port>
                   
</configuration>
               
</plugin>
           
</plugins>
       
</pluginManagement>
       
<plugins>
           
<plugin>
               
<groupId>org.apache.maven.plugins</groupId>
               
<artifactId>maven-compiler-plugin</artifactId>
               
<configuration>
                   
<source>1.8</source>
                   
<target>1.8</target>
                   
<encoding>UTF-8</encoding>
               
</configuration>
           
</plugin>
       
</plugins>
   
</build>

   
<dependencyManagement>
       
<dependencies>
           
<dependency>
               
<groupId>org.jboss.bom</groupId>
               
<artifactId>jboss-eap-javaee7-with-tools</artifactId>
               
<version>${version.jboss.bom.eap}</version>
               
<scope>import</scope>
               
<type>pom</type>
           
</dependency>
       
</dependencies>
   
</dependencyManagement>

   
<dependencies>
       
<dependency>
           
<groupId>org.jboss.spec.javax.ws.rs</groupId>
           
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
           
<scope>provided</scope>
       
</dependency>
       
<dependency>
           
<groupId>org.jboss.resteasy</groupId>
           
<artifactId>resteasy-jackson2-provider</artifactId>
           
<scope>provided</scope>
       
</dependency>
       
<dependency>
           
<groupId>org.jboss.resteasy</groupId>
           
<artifactId>resteasy-jaxrs</artifactId>
           
<scope>provided</scope>
       
</dependency>
       
<dependency>
           
<groupId>io.swagger</groupId>
           
<artifactId>swagger-jaxrs</artifactId>
           
<version>1.5.13</version>
       
</dependency>
       
<dependency>
           
<groupId>javax.servlet</groupId>
           
<artifactId>javax.servlet-api</artifactId>
           
<version>3.1.0.redhat-1</version>
           
<scope>provided</scope>
       
</dependency>
   
</dependencies>
</project>

RestActivator.java
package org.example.un.rest;

import java.util.HashSet;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

import org.reflections.Reflections;

import io.swagger.annotations.Api;
import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.models.Info;
import io.swagger.models.Swagger;

@WebListener
@ApplicationPath("/rest")
public class RestActivator extends Application implements ServletContextListener {
   
   
@Override
   
public Set<Class<?>> getClasses() {
       
Set<Class<?>> classes = new HashSet<>(super.getClasses());
        classes
.add(io.swagger.jaxrs.listing.ApiListingResource.class);
        classes
.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
       
Reflections reflections = new Reflections(RestActivator.class.getPackage().getName());
        classes
.addAll(reflections.getTypesAnnotatedWith(Api.class));
       
return classes;
   
}
   
   
private Swagger configSwagger(ServletContext contexto) {
       
Swagger swagger = new Swagger().info(this.getSwaggerInfos());
       
return swagger;
   
}
   
   
private Info getSwaggerInfos() {
       
BeanConfig config = new BeanConfig();
        config
.setVersion("0.1");
        config
.setResourcePackage(RestActivator.class.getPackage().getName());
        config
.setPrettyPrint(true);
        config
.setScan();
       
return config.getInfo();
   
}
   
   
@Override
   
public void contextInitialized(ServletContextEvent sce) {
       
ServletContext contexto = sce.getServletContext();
       
Swagger swagger = this.configSwagger(contexto);
        contexto
.setAttribute("swagger", swagger);
   
}

   
@Override
   
public void contextDestroyed(ServletContextEvent sce) {
   
}
   
}

Endpoint.java
package org.example.un.rest.ed;

import javax.validation.Valid;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.example.un.rest.ed.params.MyParams;
import org.example.un.rest.json.Body;

import io.swagger.annotations.Api;

@Path("/ed")
@Api("ed")
@Produces({ MediaType.APPLICATION_JSON })
public class Endpoint {

   
@POST
   
@Consumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN })
   
public Response post(@BeanParam MyParams params, @Valid Body body) {
       
//Do things with params.getOauthToken(), params.getUriInfo() and body
       
return Response.ok().build();
   
}

}

MyParams.java
package org.example.un.rest.ed.params;

import javax.ws.rs.HeaderParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;

import io.swagger.annotations.ApiParam;

public class MyParams {
   
   
@HeaderParam("Authorization")
   
@ApiParam(hidden = true) //It must be true, since after authorization swagger sends the token
   
private String oauthToken;
   
   
@Context
   
private UriInfo uriInfo;

   
public String getOauthToken() {
       
return oauthToken;
   
}

   
public void setOauthToken(String oauthToken) {
       
this.oauthToken = oauthToken;
   
}

   
public UriInfo getUriInfo() {
       
return uriInfo;
   
}

   
public void setUriInfo(UriInfo uriInfo) {
       
this.uriInfo = uriInfo;
   
}

}

Body.java
package org.example.un.rest.json;

import javax.validation.constraints.NotNull;

public class Body {
   
   
@NotNull
   
private String firstField;
   
@NotNull
   
private String secondField;

   
public String getFirstField() {
       
return firstField;
   
}
   
   
public String getSecondField() {
       
return secondField;
   
}

}

I am using JBOSS EAP 7 to host the application. Thanks for the attention and sorry about any language mistakes, English is not my native language.


tony tam

unread,
Apr 6, 2017, 9:11:32 AM4/6/17
to <swagger-swaggersocket@googlegroups.com>
You need to set your @BeanParam properties to be either @HeaderParam, @PathParam or @QueryParam.  Without one of these modifiers, it will be treated as a body parameter.  In the definition there can be only one body param

--
You received this message because you are subscribed to the Google Groups "Swagger" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Théo A. Monteiro

unread,
Apr 6, 2017, 9:19:33 AM4/6/17
to Swagger
In MyParams.java in the example the properties are @HeaderParam and @Context. Why swagger-core is parsing as another body?
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsub...@googlegroups.com.

tony tam

unread,
Apr 6, 2017, 9:20:38 AM4/6/17
to swagger-sw...@googlegroups.com
Try putting the annotation on the getter vs. the private field.

On Apr 6, 2017, at 9:19 AM, Théo A. Monteiro <theoalve...@gmail.com> wrote:

In MyParams.java in the example the properties are @HeaderParam and @Context. Why swagger-core is parsing as another body?

On Thursday, April 6, 2017 at 10:11:32 AM UTC-3, tony tam wrote:
You need to set your @BeanParam properties to be either @HeaderParam, @PathParam or @QueryParam.  Without one of these modifiers, it will be treated as a body parameter.  In the definition there can be only one body param
On Apr 6, 2017, at 9:06 AM, Théo A. Monteiro <theoalve...@gmail.com> wrote:

Hi,

Swagger is creating two body fields when I try to use a @BeanParam and a JSON body. 


I tried to use swagger-jaxrs and swagger-jersey2-jaxrs [1][2], but both result the same problem. Is there any way to circumvent this?

[1]. https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-JAX-RS-Project-Setup-1.5.X
[2]. https://github.com/swagger-api/swagger-core/issues/1720

This is a PoC that shows the problem: 

pom.xml

    
@Consumes({ MediaType.APPLICATION_JSON,MediaType.TEXT_PLAIN })
    
public Response post(@BeanParam MyParams params,@Valid Body body) {
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

Théo A. Monteiro

unread,
Apr 6, 2017, 9:48:51 AM4/6/17
to Swagger
Moving the annotations to the getters made no difference. I tried with both swagger-jaxrs and swagger-jersey2-jaxrs.

tony tam

unread,
Apr 6, 2017, 10:11:26 AM4/6/17
to swagger-sw...@googlegroups.com

Théo A. Monteiro

unread,
Apr 6, 2017, 12:33:37 PM4/6/17
to Swagger
I tried the sample.

It doesn't work either - I think it must be revised.

When I try to run jetty the index page is rendered without any CSS or JS and doesn't work.
The swagger.json generated doesn't describe the /store resource.

I tried to copy the method placeOrder into PetResource.java, since it has my scenario with a @Beanparam and a body, but it never shows on the swagger.json. It seems swagger is only processing methods with @GET, @PUT and @POST are ignored in this sample.

Ron Ratovsky

unread,
Apr 6, 2017, 6:47:41 PM4/6/17
to swagger-sw...@googlegroups.com

The example works. The operations are hidden as part of the sample to show how they can be filtered for security purposes.

Try loading the swagger.json as: http://localhost:8002/api/swagger.json?api_key=special-key

 

I can’t really tell why it doesn’t work for you.

Two suggestions:

1.       Add @ApiOperation to the operation itself.

2.       Since it looks like you’re actually just trying to hide the @BeanParam, try adding the @ApiParam(hidden=true) next to it: public Response post(@BeanParam @ApiParam(hidden-true) MyParams params,@Valid Body body)

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.


For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "Swagger" group.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.


For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "Swagger" group.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.
For more options, visit 
https://groups.google.com/d/optout.

 

--
You received this message because you are subscribed to the Google Groups "Swagger" group.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

Théo A. Monteiro

unread,
Apr 7, 2017, 7:08:51 AM4/7/17
to Swagger
Thanks for the clarification! 😃

I tried to change the placeOrder in the example to receive the MyParams and the duplication also occurred.

The important thing is: suggestion 2 worked. The PoC didn't have @ApiOperation, but my actual code does.

Now I am wondering what should be the correct or ideal behavior. Should @BeanParam be ever parsed as body? I obviously think it must never be a body.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsub...@googlegroups.com.


For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "Swagger" group.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsub...@googlegroups.com.


For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "Swagger" group.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsub...@googlegroups.com.
For more options, visit 
https://groups.google.com/d/optout.

 

--
You received this message because you are subscribed to the Google Groups "Swagger" group.

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsub...@googlegroups.com.

Tony Tam

unread,
Apr 7, 2017, 8:24:21 AM4/7/17
to swagger-sw...@googlegroups.com
Hard to say. People use BeanParam in mysterious ways and I don't see a reason why it should be different  
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages