{getToc} $title={Table of Contents}
How to apply Springfox Swagger Global Parameters on Rest API CRUD |
1. Introduction
In this tutorial, we'll look at Swagger 2 for a Spring REST web service, using
the
Springfox or springfox repo on
github implementation of the Swagger 2 specification. If you are not familiar
with Swagger,
visit its web page to learn
more.
I'm goint to apply it on my previouse existed Rest
APIs CRUD with JDBI
project. And I'm about to add a custom header in all APIs and use
default response messages globally without writing in each API method too.
Documentation is a very important part of any restful API, Swagger had made it
easy for developers to get a neat documentation for their API endpoints that
is readable for both humans and machines only with a few steps. Swagger lists
all API endpoints with detailed information about them like description,
parameters and output schema in JSON format while Swagger-UI provides a styled
interface with the ability to test the endpoints.
2. Dependency
Add the springfox dependency. The latest version can be downloaded from
Maven Central.
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency>
3. Configuration
Swagger Configuration with Global Request Parameter and Responses
Swagger configuration is done using Docket Bean, below is a simple
configuration to integrate Swagger with Spring Boot, you can add more
customization to your documentation in the Docket Bean, also you may create
more than one Docket Bean.
package com.yuthads.springdatajdbi3crud.configuration; import static com.google.common.collect.Lists.newArrayList; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.builders.RequestParameterBuilder; import springfox.documentation.builders.ResponseBuilder; import springfox.documentation.schema.ScalarType; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.ApiKey; import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.Contact; import springfox.documentation.service.ParameterType; import springfox.documentation.service.SecurityReference; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import springfox.documentation.swagger.web.OperationsSorter; import springfox.documentation.swagger.web.UiConfiguration; import springfox.documentation.swagger.web.UiConfigurationBuilder; @Configuration public class SwaggerConfig { private ApiInfo apiInfo() { return new ApiInfo("SpringFox Demo APIs", "Demo APIs with spring boot rest api by usgin jdbi3","1.0","Terms of service", new Contact("YUTHADS","https://yuthads.blogspot.com","yuthads@mail.com"), "License of API", "API license URL",Collections.emptyList()); } @Bean Docket api() { return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) .securityContexts(Arrays.asList(securityContext())) .securitySchemes(Arrays.asList(apiKey())).select() .apis(RequestHandlerSelectors .basePackage("com.yuthads.springdatajdbi3crud.controller")) .paths(PathSelectors.ant("/api/product/**")) .build().useDefaultResponseMessages(false) .globalResponses(HttpMethod.GET,newArrayList(new ResponseBuilder() .code("500").description("500 message").build(),new ResponseBuilder() .code("403").description("Forbidden!!!!!").build())) .globalRequestParameters(Arrays.asList(new RequestParameterBuilder(). name("x-global-header-1").description("Remote User") .in(ParameterType.HEADER).required(true) .query(simpleParameterSpecificationBuilder -> simpleParameterSpecificationBuilder .allowEmptyValue(false).model(modelSpecificationBuilder -> modelSpecificationBuilder .scalarModel(ScalarType.STRING))).build(), new RequestParameterBuilder().name("x-global-header-2") .description("Impersonate User").in(ParameterType.HEADER).required(false) .query(simpleParameterSpecificationBuilder -> simpleParameterSpecificationBuilder .allowEmptyValue(false).model(modelSpecificationBuilder -> modelSpecificationBuilder .scalarModel(ScalarType.STRING))).build())); } @Bean UiConfiguration uiConfig() { return UiConfigurationBuilder.builder() .operationsSorter(OperationsSorter.METHOD).build(); } private ApiKey apiKey() { return new ApiKey("apiKey", "Authorization", "header"); } private SecurityContext securityContext() { return SecurityContext.builder(). securityReferences(defaultAuth()).build(); } private List<SecurityReference> defaultAuth() { return Arrays.asList(new SecurityReference("apiKey", new AuthorizationScope[0])); } @Bean static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) { List<T> copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List<RequestMappingInfoHandlerMapping>) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } }
This library extensively uses googles guava library. For e.g. when you see newArrayList(…) its actually the guava equivalent of creating an normal array list and adding item(s) to it.
4. Explanation
The Docket bean of our application can be configured to give us more
control over the API documentation generation process.
globalRequestParameters method in the Docket builder accepts a list
of parameters that will be part of every API operation in the generated
Swagger specification.
Parameters can be instantiated using RequestParameterBuilder ,
another builder class, which has methods to accept name, description,type of
Parameter , is Required.
If any specifications need to be added to the parameter, use
SimpleParameterSpecificationBuilder.
It is not always desirable to expose the documentation for the entire API.
We can restrict Swagger’s response by passing parameters to the
apis() and paths() methods of the Docket class.
As seen above, RequestHandlerSelectors filter the API according to
the base package , class annotation, and method annotations. But can also
allows using the any or none predicates.
PathSelectors provides additional filtering with predicates, which
scan the request paths of our application. We can use any(), none(),
regex(), or ant().
In the example below, we will instruct Swagger to include only controllers
from a particular package, with specific paths, using the ant() predicate:
We don't need to add @EnableSwagger2 or
@EnableSwagger2WebMvc since we are using springfox swagger with
spring boot.
After defining the Docket bean, its select() method returns an instance of
ApiSelectorBuilder, which provides a way to control the endpoints exposed by
Swagger.
We can configure predicates for selecting RequestHandlers with the help of
RequestHandlerSelectors and PathSelectors. Using any() for both will make
documentation for our entire API available through Swagger.
In application.properties file add this property:
spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER to flip that
property back to its previous default value. But this won't help if you use
actuators which are not effected by that property.
Within Swagger’s response is a list of all controllers defined in our
application. Clicking on any of them will list the valid HTTP methods
(DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT).
Expanding each method provides additional useful data, such as response
status, content-type, and a list of parameters. It is also possible to try
each method using the UI.
Note: We've used the @EnableSwagger2WebMvc annotation to
enable Swagger, as it has replaced the @EnableSwagger2 annotation
in version 3 of the libraries.
Swagger also provides some default values in its response, which we can
customize, such as “Api Documentation”, “Created by Contact Email”, and
“Apache 2.0”.
To change these values, we can use the apiInfo(ApiInfo apiInfo) method —
the ApiInfo class that contains custom information about the API:
private ApiInfo apiInfo() { return new ApiInfo("SpringFox Demo APIs", "Demo APIs with spring boot rest api by usgin jdbi3", "1.0","Terms of service", new Contact("YUTHADS", "https://yuthads.blogspot.com", "yuthads@mail.com"), "License of API", "API license URL", Collections.emptyList()); }
Swagger
allows globally overriding response messages of HTTP methods
through Docket’s globalResponses() method.
First, we need to instruct Swagger not to use default response messages.
Suppose we want to override 500 and 403 response messages for all GET
methods.
To achieve this, some code must be added to the Docket’s initialization
block (original code is excluded for clarity):
.useDefaultResponseMessages(false) .globalResponses(HttpMethod.GET, newArrayList(new ResponseBuilder().code("500").description("500 message").build(), new ResponseBuilder().code("403").description("Forbidden!!!!!").build()));
And most of the user tries to find HTML swagger document file using
{host}/swagger-ui.html or {host}/swagger-ui those are now
removed.
Use This: {host}/swagger-ui/ to see the HTML document.
5. Demo & Testing
Let's start the application to generate the api document for the Spring Data REST APIs:
In my case, http://localhost:8080/swagger-ui/
Hope this article was helpful.
GitHub: