From Wiki:
Swagger is an open source software framework backed by a large ecosystem of tools that helps developers design, build, document, and consume RESTful Web services. While most users identify Swagger by the Swagger UI tool, the Swagger toolset includes support for automated documentation, code generation, and test case generation.
Official page: https://swagger.io/
- set of annotations which help us to "describe" REST - related stuff: rest endpoints and DTO-objects.
- Swagger UI, which can be used for calling this endpoints for testing purposes.
Example of "description" of DTO-object filed:
Example of "description" of REST-endpoint:
Example of Swagger UI, using which we can call just listed above method:
Nothing special here, just adding several packages for scanning
It's the most complicated part of application - we have to configure swagger here with metha-data of our application.
Here, all rest methods are swagger-annotated with description and errors which may be raised by it.
7. Imitation of service
I made imitation of generic service and concrete subclass.
Example of trying POST method on GROUP resource:
And after pressing "Try it out!" we will have:
Full source code can be downloaded from here.
Swagger is an open source software framework backed by a large ecosystem of tools that helps developers design, build, document, and consume RESTful Web services. While most users identify Swagger by the Swagger UI tool, the Swagger toolset includes support for automated documentation, code generation, and test case generation.
Official page: https://swagger.io/
1. What is Swagger?
In this post I'll show 2 components of Swagger:- set of annotations which help us to "describe" REST - related stuff: rest endpoints and DTO-objects.
- Swagger UI, which can be used for calling this endpoints for testing purposes.
Example of "description" of DTO-object filed:
@ApiModelProperty(notes = "Group Name") private String name;
Example of "description" of REST-endpoint:
@GET@Path("/{id}") @ApiOperation(value = "Get group by id resource.", response = Group.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Group resource found"), @ApiResponse(code = 404, message = "Group resource not found") }) public Response getGroup(@ApiParam @PathParam("id") Long id) {
Example of Swagger UI, using which we can call just listed above method:
2. build.gradle
I just generated the SpringBoot project from start.spring.io and added swagger dependency into it:buildscript { ext { springBootVersion = '1.5.7.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java'apply plugin: 'eclipse'apply plugin: 'org.springframework.boot' jar.archiveName = "SwaggerTestApp.jar"group = 'com.demien'version = '0.0.1-SNAPSHOT'sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-jersey') compile('org.springframework.boot:spring-boot-starter-web') compile group: 'io.swagger', name: 'swagger-jersey2-jaxrs', version: '1.5.16' testCompile('org.springframework.boot:spring-boot-starter-test') }
3. Main start class
Nothing special here, just adding several packages for scanning
package com.demien.swtest; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication( scanBasePackages = { "com.demien.swtest.config",
"com.demien.swtest.rest",
"com.demien.swtest.service" } ) public class SwtestApplication { public static void main(String[] args) { SpringApplication.run(SwtestApplication.class, args); } }
4. Jersey config
It's the most complicated part of application - we have to configure swagger here with metha-data of our application.
package com.demien.swtest.config; import com.demien.swtest.rest.GroupResource; import io.swagger.jaxrs.config.BeanConfig; import io.swagger.jaxrs.listing.ApiListingResource; import io.swagger.jaxrs.listing.SwaggerSerializers; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.wadl.internal.WadlResource; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @Component public class JerseyConfig extends ResourceConfig { @Value("${spring.jersey.application-path:/}") private String apiPath; public JerseyConfig() { // Register endpoints, providers, ... this.registerEndpoints(); } @PostConstruct public void init() { // Register components where DI is needed this.configureSwagger(); //this.registerEndpoints(); } private void registerEndpoints() { this.register(GroupResource.class); // Access through /<Jersey's servlet path>/application.wadl this.register(WadlResource.class); } private void configureSwagger() { // Available at localhost:port/api/swagger.json this.register(ApiListingResource.class); this.register(SwaggerSerializers.class); BeanConfig config = new BeanConfig(); config.setConfigId("springboot-jersey-swagger-test-app"); config.setTitle("Spring Boot, Jersey, Swagger Test Application"); config.setVersion("v1"); config.setContact("Dmitry Kovalsky"); config.setSchemes(new String[] { "http", "https" }); config.setBasePath(this.apiPath); config.setResourcePackage("com.demien.swtest.rest"); config.setPrettyPrint(true); config.setScan(true); } }
5. Dto and Model classes
UI is not sending ID - it will be generated on server side, what is why I need 2 classes: one for data which will be sent from UI and the second one - for response. Fields in these classes are swagger-annotated.package com.demien.swtest.dto; import io.swagger.annotations.ApiModelProperty; public class GroupDTO { @ApiModelProperty(notes = "Group Name") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.demien.swtest.model; import com.demien.swtest.dto.GroupDTO; import io.swagger.annotations.ApiModelProperty; public class Group extends GroupDTO{ @ApiModelProperty(notes = "Generated Group ID") private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Group() { } public Group(GroupDTO dto) { setName(dto.getName()); } }
6. Rest controller(resource)
Here, all rest methods are swagger-annotated with description and errors which may be raised by it. package com.demien.swtest.rest; import com.demien.swtest.dto.GroupDTO; import com.demien.swtest.model.Group; import com.demien.swtest.service.GroupService; import io.swagger.annotations.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @Component@Path("/groups") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Api(value = "Group resource", produces = "application/json") public class GroupResource { @Autowired private GroupService groupService; public Response OkResponse(Object entity) { return Response.status(Response.Status.OK).entity(entity).build(); } public Response NotFoundResponse() { return Response.status(Response.Status.NOT_FOUND).build(); } @POST @ApiOperation(value = "Create group.", response = Group.class) @ApiResponses(value = { @ApiResponse(code = 201, message = "group resource ", responseHeaders = { @ResponseHeader(name = "Location", description = "The URL to retrieve created resource", response = String.class) }) }) public Response createGroup(GroupDTO groupDTO, @Context UriInfo uriInfo) { Group result = groupService.add(new Group(groupDTO)); return OkResponse(result); } @GET @Path("/{id}") @ApiOperation(value = "Get group by id resource.", response = Group.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Group resource found"), @ApiResponse(code = 404, message = "Group resource not found") }) public Response getGroup(@ApiParam @PathParam("id") Long id) { Group result = groupService.get(id); return result == null ? NotFoundResponse() : OkResponse(result); } @PUT @ApiOperation(value = "Update group.", response = Group.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Group resource found") }) public Response updateGroup(Group group, @Context UriInfo uriInfo) { groupService.update(group.getId(), group); Group result = groupService.get(group.getId()); return OkResponse(result); } @DELETE @Path("/{id}") @ApiOperation(value = "Delete group by id resource.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Group resource found"), @ApiResponse(code = 404, message = "Group resource not found") }) public Response deleteGroup(@ApiParam @PathParam("id") Long id) { Group result = groupService.get(id); if (result == null) return NotFoundResponse(); groupService.delete(id); return OkResponse(id); } }
7. Imitation of service
I made imitation of generic service and concrete subclass.
package com.demien.swtest.service; import com.demien.swtest.model.Group; import org.springframework.stereotype.Service; import java.util.function.UnaryOperator; @Servicepublic class GroupService extends AbstractService<Group> { public GroupService() { super((e, id) -> { e.setId(id); return e; }); } }
package com.demien.swtest.service; import java.util.HashMap; import java.util.Map; import java.util.function.BiFunction; import java.util.function.UnaryOperator; public abstract class AbstractService<T> { private long id; private Map<Long,T> storage = new HashMap<>(); private BiFunction<T, Long, T> idSetter; public AbstractService(BiFunction<T, Long, T> idSetter) { this.idSetter = idSetter; } public T add(T entity) { id++; T result = idSetter.apply(entity, id); storage.put(id, result); return result; } public T get(Long id) { return storage.get(id); } public void update(Long id, T entity) { storage.put(id, entity); } public void delete(Long id) { storage.put(id, null); } }
8. swagger.json
Now we can run our application and open this address: http://localhost:8080/api/swagger.json
The result should be - json generated by swagger which "explains" our rest endpoints with
detailed description:
{
"swagger":"2.0",
"info":{
"version":"v1",
"title":"Spring Boot, Jersey, Swagger Test Application",
"contact":{
"name":"Dmitry Kovalsky"
}
},
"basePath":"/api",
"tags":[
{
"name":"Group resource"
}
],
"schemes":[
"http",
"https"
],
"paths":{
"/groups/{id}":{
"get":{
"tags":[
"Group resource"
],
"summary":"Get group by id resource.",
"description":"",
"operationId":"getGroup",
"consumes":[
"application/json"
],
"produces":[
"application/json"
],
"parameters":[
{
"name":"id",
"in":"path",
"required":true,
"type":"integer",
"format":"int64"
}
],
"responses":{
"200":{
"description":"Group resource found"
},
"404":{
"description":"Group resource not found"
}
}
},
"delete":{
"tags":[
"Group resource"
],
"summary":"Delete group by id resource.",
"description":"",
"operationId":"deleteGroup",
"consumes":[
"application/json"
],
"produces":[
"application/json"
],
"parameters":[
{
"name":"id",
"in":"path",
"required":true,
"type":"integer",
"format":"int64"
}
],
"responses":{
"200":{
"description":"Group resource found"
},
"404":{
"description":"Group resource not found"
}
}
}
},
"/groups":{
"post":{
"tags":[
"Group resource"
],
"summary":"Create group.",
"description":"",
"operationId":"createGroup",
"consumes":[
"application/json"
],
"produces":[
"application/json"
],
"parameters":[
{
"in":"body",
"name":"body",
"required":false,
"schema":{
"$ref":"#/definitions/GroupDTO"
}
}
],
"responses":{
"200":{
"description":"successful operation",
"schema":{
"$ref":"#/definitions/Group"
}
},
"201":{
"description":"group resource ",
"headers":{
"Location":{
"type":"string",
"description":"The URL to retrieve created resource"
}
}
}
}
},
"put":{
"tags":[
"Group resource"
],
"summary":"Update group.",
"description":"",
"operationId":"updateGroup",
"consumes":[
"application/json"
],
"produces":[
"application/json"
],
"parameters":[
{
"in":"body",
"name":"body",
"required":false,
"schema":{
"$ref":"#/definitions/Group"
}
}
],
"responses":{
"200":{
"description":"Group resource found"
}
}
}
}
},
"definitions":{
"Group":{
"type":"object",
"properties":{
"name":{
"type":"string"
},
"id":{
"type":"integer",
"format":"int64"
}
}
},
"GroupDTO":{
"type":"object",
"properties":{
"name":{
"type":"string"
}
}
}
}
}
9. Swagger UI
JSON with endpoint description - is great! But swagger can even more: based on this JSON, it can provide the UI to call these endpoints. We just have to download it from https://swagger.io/swagger-ui/ and put into src/main/resource/static. In will be available by address: http://localhost:8080/index.html.Example of trying POST method on GROUP resource:
And after pressing "Try it out!" we will have:
10. The end
Full source code can be downloaded from here.