Chapter 54. Extending JAX-RS Endpoints with Swagger Support
Abstract
The CXF Swagger2Feature (org.apache.cxf.jaxrs.swagger.Swagger2Feature
) allows you to generate Swagger 2.0 documents by extending published JAX-RS service endpoints with a simple configuration.
The Swagger2Feature is supported in both Spring Boot and Karaf implementations.
54.1. Swagger2Feature options
You can use the following options in Swagger2Feature.
Name | Description | Default |
---|---|---|
|
The context root path+ (see also the | null |
| Your contact information+ | |
| A description+ | "The Application" |
| A security filter+ | null |
| The host and port information+ | null |
|
Excludes specific paths when scanning all resources (see the | null |
| The license+ | "Apache 2.0 License" |
| The license URL+ | |
|
When generating | false |
| A list of comma separated package names where resources must be scanned+ | A list of service classes configured at the endpoint |
| Runs the feature as a filter | false |
| Generates the swagger documentation+ | true |
|
Scans all resources including non-annotated JAX-RS resources (see also the | false |
| The protocol schemes+ | null |
| Swagger UI configuration | null |
| The terms of service URL+ | null |
| The title+ | "Sample REST Application" |
|
Prevents Swagger from caching the value of the | false |
| The version+ | "1.0.0" |
+ The option is defined in Swagger’s BeanConfig
++ The option is defined in Swagger’s ReaderConfig
=== Karaf Implementations
This section describes how to use the Swagger2Feature in which REST services are defined inside JAR files and deployed to a Fuse on Karaf container.
==== Quickstart example
You can download Red Hat Fuse quickstarts
from the Fuse Software Downloads page.
The Quickstart zip file contains a /cxf/rest/
directory for a quickstart that demonstrates how to create a RESTful (JAX-RS) web service using CXF and how to enable Swagger and annotate the JAX-RS endpoints.
==== Enabling Swagger
Enabling Swagger involves:
Modifying the XML file that defines the CXF service by adding the CXF class (
org.apache.cxf.jaxrs.swagger.Swagger2Feature
) to the<jaxrs:server>
definition.For an example, see Example 55.4 Example XML file.
In the REST resource class:
Importing the Swagger API annotations for each annotation required by the service:
import io.swagger.annotations.*
where * =
Api
,ApiOperation
,ApiParam
,ApiResponse
,ApiResponses
, and so on.For details, go to
https://github.com/swagger-api/swagger-core/wiki/Annotations
.For an example, see Example 55.5 Example Resource class.
-
Adding Swagger annotations to the JAX-RS annotated endpoints (
@PATH
,@PUT
,@POST
,@GET
,@Produces
,@Consumes
,@DELETE
,@PathParam
, and so on).
For an example, see Example 55.5 Example Resource class.
Example 55.4 Example XML file
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs" xmlns:cxf="http://cxf.apache.org/blueprint/core" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd"> <jaxrs:server id="customerService" address="/crm"> <jaxrs:serviceBeans> <ref component-id="customerSvc"/> </jaxrs:serviceBeans> <jaxrs:providers> <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/> </jaxrs:providers> <jaxrs:features> <bean class="org.apache.cxf.jaxrs.swagger.Swagger2Feature"> <property name="title" value="Fuse:CXF:Quickstarts - Customer Service" /> <property name="description" value="Sample REST-based Customer Service" /> <property name="version" value="${project.version}" /> </bean> </jaxrs:features> </jaxrs:server> <cxf:bus> <cxf:features> <cxf:logging /> </cxf:features> <cxf:properties> <entry key="skip.default.json.provider.registration" value="true" /> </cxf:properties> </cxf:bus> <bean id="customerSvc" class="org.jboss.fuse.quickstarts.cxf.rest.CustomerService"/> </blueprint>
Example 55.5 Example Resource class
. . . import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; . . . @Path("/customerservice/") @Api(value = "/customerservice", description = "Operations about customerservice") public class CustomerService { private static final Logger LOG = LoggerFactory.getLogger(CustomerService.class); private MessageContext jaxrsContext; private long currentId = 123; private Map<Long, Customer> customers = new HashMap<>(); private Map<Long, Order> orders = new HashMap<>(); public CustomerService() { init(); } @GET @Path("/customers/{id}/") @Produces("application/xml") @ApiOperation(value = "Find Customer by ID", notes = "More notes about this method", response = Customer.class) @ApiResponses(value = { @ApiResponse(code = 500, message = "Invalid ID supplied"), @ApiResponse(code = 204, message = "Customer not found") }) public Customer getCustomer(@ApiParam(value = "ID of Customer to fetch", required = true) @PathParam("id") String id) { LOG.info("Invoking getCustomer, Customer id is: {}", id); long idNumber = Long.parseLong(id); return customers.get(idNumber); } @PUT @Path("/customers/") @Consumes({ "application/xml", "application/json" }) @ApiOperation(value = "Update an existing Customer") @ApiResponses(value = { @ApiResponse(code = 500, message = "Invalid ID supplied"), @ApiResponse(code = 204, message = "Customer not found") }) public Response updateCustomer(@ApiParam(value = "Customer object that needs to be updated", required = true) Customer customer) { LOG.info("Invoking updateCustomer, Customer name is: {}", customer.getName()); Customer c = customers.get(customer.getId()); Response r; if (c != null) { customers.put(customer.getId(), customer); r = Response.ok().build(); } else { r = Response.notModified().build(); } return r; } @POST @Path("/customers/") @Consumes({ "application/xml", "application/json" }) @ApiOperation(value = "Add a new Customer") @ApiResponses(value = { @ApiResponse(code = 500, message = "Invalid ID supplied"), }) public Response addCustomer(@ApiParam(value = "Customer object that needs to be updated", required = true) Customer customer) { LOG.info("Invoking addCustomer, Customer name is: {}", customer.getName()); customer.setId(++currentId); customers.put(customer.getId(), customer); if (jaxrsContext.getHttpHeaders().getMediaType().getSubtype().equals("json")) { return Response.ok().type("application/json").entity(customer).build(); } else { return Response.ok().type("application/xml").entity(customer).build(); } } @DELETE @Path("/customers/{id}/") @ApiOperation(value = "Delete Customer") @ApiResponses(value = { @ApiResponse(code = 500, message = "Invalid ID supplied"), @ApiResponse(code = 204, message = "Customer not found") }) public Response deleteCustomer(@ApiParam(value = "ID of Customer to delete", required = true) @PathParam("id") String id) { LOG.info("Invoking deleteCustomer, Customer id is: {}", id); long idNumber = Long.parseLong(id); Customer c = customers.get(idNumber); Response r; if (c != null) { r = Response.ok().build(); customers.remove(idNumber); } else { r = Response.notModified().build(); } return r; } . . . }
=== Spring Boot Implementations
This section describes how to use the Swagger2Feature in Spring Boot.
==== Quickstart example
The Quickstart example (https://github.com/fabric8-quickstarts/spring-boot-cxf-jaxrs
) demonstrates how you can use Apache CXF with Spring Boot. The Quickstart uses Spring Boot to configure an application that includes a CXF JAX-RS endpoint with Swagger enabled.
==== Enabling Swagger
Enabling Swagger involves:
In the REST application:
Importing Swagger2Feature:
import org.apache.cxf.jaxrs.swagger.Swagger2Feature;
Adding Swagger2Feature to a CXF endpoint:
endpoint.setFeatures(Arrays.asList(new Swagger2Feature()));
For an example, see Example 55.1 Example REST application.
In the Java implementation file, importing the Swagger API annotations for each annotation required by the service:
import io.swagger.annotations.*
where * =
Api
,ApiOperation
,ApiParam
,ApiResponse
,ApiResponses
, and so on.For details, see
https://github.com/swagger-api/swagger-core/wiki/Annotations
.For an example, see Example 55.2 Example Java implementation file.
In the Java file, adding Swagger annotations to the JAX-RS annotated endpoints (
@PATH
,@PUT
,@POST
,@GET
,@Produces
,@Consumes
,@DELETE
,@PathParam
, and so on).For an example, see Example 55.3 Example Java file.
Example 55.1 Example REST application
package io.fabric8.quickstarts.cxf.jaxrs; import java.util.Arrays; import org.apache.cxf.Bus; import org.apache.cxf.endpoint.Server; import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; import org.apache.cxf.jaxrs.swagger.Swagger2Feature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SampleRestApplication { @Autowired private Bus bus; public static void main(String[] args) { SpringApplication.run(SampleRestApplication.class, args); } @Bean public Server rsServer() { // setup CXF-RS JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean(); endpoint.setBus(bus); endpoint.setServiceBeans(Arrays.<Object>asList(new HelloServiceImpl())); endpoint.setAddress("/"); endpoint.setFeatures(Arrays.asList(new Swagger2Feature())); return endpoint.create(); } }
Example 55.2 Example Java implementation file
import io.swagger.annotations.Api; @Api("/sayHello") public class HelloServiceImpl implements HelloService { public String welcome() { return "Welcome to the CXF RS Spring Boot application, append /{name} to call the hello service"; } public String sayHello(String a) { return "Hello " + a + ", Welcome to CXF RS Spring Boot World!!!"; } }
Example 55.3 Example Java file
package io.fabric8.quickstarts.cxf.jaxrs; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.springframework.stereotype.Service; @Path("/sayHello") @Service public interface HelloService { @GET @Path("") @Produces(MediaType.TEXT_PLAIN) String welcome(); @GET @Path("/{a}") @Produces(MediaType.TEXT_PLAIN) String sayHello(@PathParam("a") String a); }
54.2. Accessing Swagger Documents
When Swagger is enabled by Swagger2Feature, the Swagger documents are available at the location URL constructed of the service endpoint location followed by /swagger.json
or /swagger.yaml
.
For example, for a JAX-RS endpoint that is published at http://host:port/context/services/
where context
is a web application context and /services
is a servlet URL, its Swagger documents are available at http://host:port/context/services/swagger.json
and http://host:port/context/services/swagger.yaml
.
If Swagger2Feature is active, the CXF Services page links to Swagger documents.
In the above example, you would go to http://host:port/context/services/services
and then follow a Swagger link which returns a Swagger JSON document.
If CORS support is needed to access the definition from a Swagger UI on another host, you can add the CrossOriginResourceSharingFilter
from cxf-rt-rs-security-cors
.
54.3. Accessing Swagger through a reverse proxy
If you want to access a Swagger JSON document or a Swagger UI through a reverse proxy, set the following options:
Set the
CXFServlet use-x-forwarded-headers
init parameter to true.In Spring Boot, prefix the parameter name with
cxf.servlet.init
:cxf.servlet.init.use-x-forwarded-headers=true
In Karaf, add the following line to the
installDir/etc/org.apache.cxf.osgi.cfg
configuration file:cxf.servlet.init.use-x-forwarded-headers=true
Note: If you do not already have an
org.apache.cxf.osgi.cfg
file in youretc
directory, you can create one.
If you specify a value for the Swagger2Feature
basePath
option and you want to prevent Swagger from caching thebasePath
value, set the Swagger2FeatureusePathBasedConfig
option to TRUE:<bean class="org.apache.cxf.jaxrs.swagger.Swagger2Feature"> <property name="usePathBasedConfig" value="TRUE" /> </bean>