-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Swagger 2.X Extensions
NOTE: Swagger Core 2.X produces OpenApi 3.0 definition files. For more information, check out the OpenAPI specification repository. If you're looking for swagger 1.5.X and OpenApi 2.0, please refer to 1.5.X JAX-RS Setup
NOTE: Since version 2.2.0 Swagger Core supports OpenAPI 3.1; see this page for details
Since version 2.1.7 Swagger Core supports also Jakarta namespace, with a parallel set of artifacts with -jakarta
suffix, providing the same functionality as the "standard" javax
namespace ones.
While behaviour described in this documentation is the same for both namespaces, artifact IDs, JEE / Jakarta EE versions and Jackson versions mentioned
refer to javax
namespace.
If you are using jakarta namespace:
- when you read artifact IDs in the form:
swagger-*
(e.g.swagger-core
), replace them withswagger-*-jakarta
(e.g.swagger-core-jakarta
) - when you read
javax.*
in package names, replace that withjakarta
(e.gjakarta.ws.rs.GET
) - when JEE / Jakarta EE dependencies are provided in examples, replace their version with Jakarta EE 9 versions.
- When Jackson dependencies are provided in examples, add the
jakarta
classifier for artifacts supporting it. See Jackson release notesJakarta
namespace Swagger Core artifacts need Jackson 2.12+
If for any reason you need to customise the generated OpenAPI definition beyond what is possible with the annotations, there are different ways to do it (also applying more than one at the same time):
You can provide the Swagger engine with a ReaderListener
that provides the corresponding callbacks:
public interface ReaderListener {
/**
* Called before the OpenAPI definition gets populated from scanned classes. Use this method to
* pre-process the OpenAPI definition before it gets populated.
*
* @param reader the reader used to read annotations and build the OpenAPI definition
* @param openAPI the initial OpenAPI definition
*/
void beforeScan(Reader reader, OpenAPI openAPI);
/**
* Called after a OpenAPI definition has been populated from scanned classes. Use this method to
* post-process OpenAPI definitions.
*
* @param reader the reader used to read annotations and build the OpenAPI definition
* @param openAPI the configured OpenAPI definition
*/
void afterScan(Reader reader, OpenAPI openAPI);
}
Any class found during resource scanning implementing this interface will be instantiated and invoked correspondingly. For example the following class:
public class BasePathModifier implements ReaderListener {
void beforeScan(Reader reader, Swagger openAPI){}
void afterScan(Reader reader, Swagger openAPI){
openAPI.getInfo().setTitle(System.getProperty( "swagger.title", openAPI.getInfo().getTitle() ));
}
}
Would allow you to override the title from a system-property.
You can provide an OpenAPISpecFilter implementation in Configuration to be able to filter out or even completely change the resolved definition.
When implementing the filter, extending AbstractSpecFilter is probably the most convenient option, as it allows to override only the needed filter method.
A simple example of a filter could be:
/**
* Sample filter to avoid all resources for the /user resource
**/
public class NoPetOperationsFilter extends AbstractSpecFilter {
public static final String PET_RESOURCE = "/pet";
@Override
public Optional<Operation> filterOperation(Operation operation, ApiDescription api, Map<String, List<String>> params, Map<String, String> cookies, Map<String, List<String>> headers) {
if (api.getPath().startsWith(PET_RESOURCE)) {
return Optional.empty();
}
return Optional.of(operation);
}
}
check out the tests for some additional examples of filter usage.
When relying on provided JAX-RS resources to expose the API (since version 2.0.8 also using maven or gradle plugin), the filter defined in configuration will be applied automatically, otherwise, e.g. when serving the definition from own servlet or resource, you can apply it for example like:
OpenAPI oas = resolveDefinition()
OpenAPISpecFilter filterImpl = my.custom.Filter;
SpecFilter f = new SpecFilter();
oas = f.filter(oas, filterImpl, getQueryParams(uriInfo.getQueryParameters()), getCookies(headers),
getHeaders(headers));
Please check out Resource scanning
As mentioned in Resolve the OpenAPI Definition, the default JAX-RS reader is responsible for the core logic which builds the OpenAPI definition out of JAX-RS resources and applied configuration.
In cases where you want to customize such logic (e.g. you might want to also consider custom annotations, or any other available metadata), you can provide your own implementation of Reader, implementing OpenApiReader interface and providing the qualified class name in the Configuration:
readerClass: my.custom.MyReader
prettyPrint: true
You can either extend default Reader
e.g. to customize the logic resolving the application path:
class MyReader extends Reader {
@Override
protected String resolveApplicationPath() {
return "custom logic to resolve path";
}
}
or you can provide a completely customized reader implementation with the same mechanism by directly implementing OpenApiReader
, for example in non JAX-RS environments.
swagger-core
converter/resolver logic is responsible for resolving types into OpenAPI schemas.
This applies e.g to response schemas, request body, parameters, etc, and uses as input the POJO classes together with available metadata, including various
annotations (e.g. Swagger, JAX-RS, Jackson, JAXB, etc.).
You can customize such logic by implementing your own ModelConverter (or chain of these, e.g. each handling a different type):
public class SamplePropertyConverter implements ModelConverter {
@Override
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
if (type.isSchemaProperty()) {
JavaType _type = Json.mapper().constructType(type.getType());
if (_type != null) {
Class<?> cls = _type.getRawClass();
if (MyCustomClass.class.isAssignableFrom(cls)) {
return new DateTimeSchema();
}
}
}
if (chain.hasNext()) {
return chain.next().resolve(type, context, chain);
} else {
return null;
}
}
}
and registering it:
ModelConverters.getInstance().addConverter(new SamplePropertyConverter());
Check out test samples for same more examples.
You can also extend ModelResolver (default implementation of ModelConverter
) and override specific resolve..
methods to fine tune only a subset of definition properties, e.g.:
public class MyCustomResolver extends ModelResolver {
public MyCustomResolver(ObjectMapper mapper) {
super(mapper);
}
@Override
protected Object resolveExample(Annotated a, Annotation[] annotations, io.swagger.v3.oas.annotations.media.Schema schema) {
if (a.getAnnotation(CustomAnnotation.class) != null) {
return customResolveExampleFromCustomAnnotation(a.getAnnotation(CustomAnnotation.class));
}
return super.resolveExample(a, annotations, schema);
}
}
and register it:
ModelConverters.getInstance().addConverter(new MyCustomResolver(Json.mapper()));