I have integrated swagger with spring webflux with below steps:
added gradle dependency
implementation("io.springfox:springfox-swagger2:3.0.0-SNAPSHOT")
declared Docket bean
import springfox.documentation.swagger2.annotations.EnableSwagger2WebFlux
#Configuration
#EnableSwagger2WebFlux
class SpringFoxConfig {
#Bean
fun api(): Docket {
return Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
}
}
declared controller with swagger annotations
import io.swagger.annotations.Api
import org.springframework.web.bind.annotation.*
#RestController
#Api(value = "healthCheck", description = "API to check service heartbeat", tags = ["APIs"])
#RequestMapping("/spring/app")
class HealthCheckController {
#RequestMapping(method = [RequestMethod.GET, RequestMethod.POST], value = ["/ping"])
suspend fun ping(): String {
return "I am alive!"
}
}
Run the app and access url at /swagger-ui.html, it is showing swagger ui with rest endpoint
But it is showing white label error page with 404 when CORS are enabled with below code
import org.springframework.context.annotation.Configuration
import org.springframework.web.reactive.config.CorsRegistry
import org.springframework.web.reactive.config.EnableWebFlux
import org.springframework.web.reactive.config.WebFluxConfigurer
#Configuration
#EnableWebFlux
class WebConfig: WebFluxConfigurer
{
//Refer: https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
override fun addCorsMappings(registry: CorsRegistry)
{
registry.addMapping("/**")
.allowedOrigins("*") // any host or put domain(s) here
.allowCredentials(false).maxAge(3600);
// .allowedMethods("GET, POST") // put the http verbs you want allow
// .allowedHeaders("Authorization") // put the http headers you want allow
}
}
Can someone help what i am doing wrong?
Related
I'm integrating with an ancient service that is adds jsessionid to the URL and redirects to it. I'm using RestTemplate to talk to the service.
The problem is that it follows the redirect forever, since I'm not setting the jsession cookie.
How do I turn off following the redirects in Spring RestTemplate?
I figured out one way to do it, don't know if this is the preferred way to do it.
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
final HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory();
CloseableHttpClient build =
HttpClientBuilder.create().disableRedirectHandling().build();
factory.setHttpClient(build);
restTemplate.setRequestFactory(factory);
return restTemplate;
}
You can also write your own HttpRequestFactory class and override the default behaviour:
class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
connection.setInstanceFollowRedirects(false);
}
}
#Configuration
public class AppConfig {
#Bean
public RestTemplate httpClient(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofSeconds(10))
.setReadTimeout(Duration.ofSeconds(10))
.requestFactory(CustomClientHttpRequestFactory.class)
.build();
}
}
Adding my solution as I found the others above not fitting the current Spring 5.x and Spring Boot 2.7.
The important aspect is that Spring can be configured with around 6 different HTTP client libraries. The way to configure them differs, and is done by using the client lib's API directly.
Below is an example for Apache HttpClient (historically called HttpCompoments) used to create a RestTemplate for a test. The syntax is Kotlin.
import org.apache.http.client.HttpClient
import org.apache.http.impl.client.HttpClients
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.web.client.RestTemplate
#Bean #Qualifier("forTests")
fun restTemplateHttpsWithTestTrustStore(builder: RestTemplateBuilder): RestTemplate {
val httpClient: HttpClient = HttpClients.custom()
.disableRedirectHandling()
.build()
return builder.requestFactory { HttpComponentsClientHttpRequestFactory(httpClient) }.build()
}
And then the test:
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
)
class HttpsWorksTest {
#LocalServerPort private var port = 0
#Autowired #Qualifier("forTests") private lateinit var restTemplateTrustStore: RestTemplate
#Test
fun httpsSelfRequestTest() {
var url = "$proto://localhost:$port/someRediirectingEndpoint"
log.info("Requesting: GET $url")
var response = restTemplateTrustStore.getForEntity(url, String::class.java)
Assertions.assertThat(response.statusCode).isEqualTo(HttpStatus.SEE_OTHER)
}
}
I have a Swagger config like this
#EnableSwagger2
#Configuration
public class SwaggerConfig {
#Bean
public Docket api() {
List<SecurityScheme> schemeList = new ArrayList<>();
schemeList.add(new ApiKey(HttpHeaders.AUTHORIZATION, "JWT", "header"));
return new Docket(DocumentationType.SWAGGER_2)
.produces(Collections.singleton("application/json"))
.consumes(Collections.singleton("application/json"))
.ignoredParameterTypes(Authentication.class)
.securitySchemes(schemeList)
.useDefaultResponseMessages(false)
.select()
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot")))
.paths(PathSelectors.any())
.build();
}
}
In the Swagger UI when I click on the Authorize button I enter my JWT token in the value field eyJhbGc..nN84qrBg. Now I expect that any request I do through the Swagger UI will contain the JWT in the header. However, that is not the case.
No request contains a Authorization header.
What am I missing?
Original answer
Support for Authorization: Bearer [JWT_TOKEN] header is working as of version 2.9.2
Added the following dependencies to build.gradle
compile("io.springfox:springfox-swagger2:2.9.2") {
exclude module: 'mapstruct' // necessary in my case to not end up with multiple mapstruct versions
}
compile "io.springfox:springfox-bean-validators:2.9.2"
compile "io.springfox:springfox-swagger-ui:2.9.2"
Configured Swagger via
#Configuration
#EnableSwagger2
#Import(springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration.class)
public class SwaggerConfiguration {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String DEFAULT_INCLUDE_PATTERN = "/api/.*";
private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class);
#Bean
public Docket swaggerSpringfoxDocket() {
log.debug("Starting Swagger");
Contact contact = new Contact(
"Matyas Albert-Nagy",
"https://justrocket.de",
"matyas#justrocket.de");
List<VendorExtension> vext = new ArrayList<>();
ApiInfo apiInfo = new ApiInfo(
"Backend API",
"This is the best stuff since sliced bread - API",
"6.6.6",
"https://justrocket.de",
contact,
"MIT",
"https://justrocket.de",
vext);
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.pathMapping("/")
.apiInfo(ApiInfo.DEFAULT)
.forCodeGeneration(true)
.genericModelSubstitutes(ResponseEntity.class)
.ignoredParameterTypes(Pageable.class)
.ignoredParameterTypes(java.sql.Date.class)
.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(java.time.ZonedDateTime.class, Date.class)
.directModelSubstitute(java.time.LocalDateTime.class, Date.class)
.securityContexts(Lists.newArrayList(securityContext()))
.securitySchemes(Lists.newArrayList(apiKey()))
.useDefaultResponseMessages(false);
docket = docket.select()
.paths(regex(DEFAULT_INCLUDE_PATTERN))
.build();
watch.stop();
log.debug("Started Swagger in {} ms", watch.getTotalTimeMillis());
return docket;
}
private ApiKey apiKey() {
return new ApiKey("JWT", AUTHORIZATION_HEADER, "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(DEFAULT_INCLUDE_PATTERN))
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(
new SecurityReference("JWT", authorizationScopes));
}
}
Access the ui via http://host:port/<context-root>/swagger-ui.html
Press Authorize all requests and enter Bearer [JWT_TOKEN]
Voila your next requests will have the JWT header
Update 2022-09-24
After a series of newer projects, I started using springdoc-openapi that generates docs based on javadoc, eliminating the need of extra annotations.
Writing this for anyone who is willing to give this library a try. I would recommend it/am a happy user of this lib.
Dependencies
build.gradle
[...]
// swagger ui
implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
implementation 'org.springdoc:springdoc-openapi-javadoc:1.6.9'
annotationProcessor 'com.github.therapi:therapi-runtime-javadoc-scribe:0.13.0'
implementation 'com.github.therapi:therapi-runtime-javadoc:0.13.0'
[...]
Declare Authentication
Using the project specific SecurityConfiguration.java - define the pattern of the OpenAPI authorization. This case: Bearer in Authorization in the HTTP header.
import static io.swagger.v3.oas.annotations.enums.SecuritySchemeIn.HEADER;
import static io.swagger.v3.oas.annotations.enums.SecuritySchemeType.HTTP;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
#Component
#SecurityScheme(name = SecurityConfiguration.SECURITY_CONFIG_NAME, in = HEADER, type = HTTP, scheme = "bearer", bearerFormat = "JWT")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
[...]
public static final String SECURITY_CONFIG_NAME = "App Bearer token";
[...]
Usage in REST controllers
Usage in SomeController.java shall reference the security config
import static com.x.common.security.SecurityConfiguration.SECURITY_CONFIG_NAME;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
#RestController
#RequestMapping("/api/v1/resources")
#SecurityRequirement(name = SECURITY_CONFIG_NAME)
public class ConnectionSyncController {
/**
* Documentation that will be rendered
*
* supports
*
* 1. markdown
* 1. list
*/
#PostMapping("/{id}/sync")
#DomainAuthorize(permissionType = BasePermissions.PERM_ADMIN_OPERATIONS)
public void syncConnection(#PathVariable("id") Long id) {
Configure reachability
Configure location of openapi specs (swagger yml) - default /v3/api-docs
Configure where swagger-ui is located/loads config from
Configure which backends swagger-ui can talk with
In case we are behind a proxy, we need to make sure that the calls are proxied with correct headers for everything to work.
/src/main/resources/application.yml
server:
port: 80
# needed for swagger-ui to detect correct proxied paths correctly.
# Configuration needed for the [Try out] buttons to work
# this works in combination with the proxied headers
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Prefix /services/impower-facilioo;
forward-headers-strategy: FRAMEWORK
springdoc:
swagger-ui:
# where the UI configuration is located at
configUrl: /[some/public/path]/v3/api-docs/swagger-config
filter: true
deepLinking: true
# where the server API yml/json files are at (dropdown in top right corner)
urls[0]:
url: /[some/public/path]/v3/api-docs
name: backend
For swagger version 2.9.2
Create a SwaggerConfig class.
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
.securitySchemes(Arrays.asList(apiKey()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Sig-Predict REST API Document")
.description("work in progress")
.termsOfServiceUrl("localhost")
.version("1.0")
.build();
}
private ApiKey apiKey() {
return new ApiKey("jwtToken", "Authorization", "header");
}
Then annotate each API you would like to send this Authorization header to with:
#ApiOperation(value = "", authorizations = { #Authorization(value="jwtToken") })
Your code is correct.
There is a bug in springfox-swagger-ui/springfox-swagger2 version 2.8.0 and it seems 2.9.2 as well. I suspect you are using a version effected by this bug.
I simply downgraded to 2.7.0 and it worked perfectly.
For a quick solution, I configured my docket with a global parameter authorization header in my swaggerConfig class.
#Configuration
#EnableSwagger2
public class SwaggerConfig {
private static final Set<String> DEFAULT_PRODUCES_CONSUMES = new HashSet<String>(Arrays.asList("application/json"));
#Bean
public Docket api() {
ParameterBuilder parameterBuilder = new ParameterBuilder();
parameterBuilder.name("Authorization")
.modelRef(new ModelRef("string"))
.parameterType("header")
.description("JWT token")
.required(true)
.build();
List<Parameter> parameters = new ArrayList<>();
parameters.add(parameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(DEFAULT_API_INFO)
.produces(DEFAULT_PRODUCES_CONSUMES)
.consumes(DEFAULT_PRODUCES_CONSUMES)
.select()
.build()
// Setting globalOperationParameters ensures that authentication header is applied to all APIs
.globalOperationParameters(parameters);
}
}
Wrote a small post authorization-field-in-swagger-ui about this.
Please try something like below
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any()).paths(PathSelectors.regex("/api/v1/.*"))
.build().groupName("API")
.globalOperationParameters(newArrayList(
new ParameterBuilder().name(HttpHeaders.AUTHORIZATION).description("Authorization token").required(true)
.modelRef(new ModelRef("string")).parameterType("header").required(true).build()))
.apiInfo(apiInfo());
Where the accepted answer is correct, it has a small flaw. You have to manually add 'Bearer '-text in the authorization value to make the token work correctly (when the prefix is expected like in my case).
Did some research to improve this and got this working with using the OpenApi without the need for that tiny nasty addition.
Source I used to go on with this (Made some minor changes/additions)
In pom.xml I have the following:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.5</version>
<relativePath />
</parent>
<properties>
<java.version>16</java.version>
<swagger.version>2.9.2</swagger.version>
<open.api.version>1.6.9</open.api.version>
</properties>
<dependencies>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${open.api.version}</version>
</dependency>
</dependencies>
To the application.properties I added few lines (optional):
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
springdoc.swagger-ui.path=swagger-ui.html
springdoc.paths-to-exclude=/swagger-resources/**
The swagger needed to have some security setting exceptions:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/* Specify the urls not requiring authentication. */
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/configuration/ui", "/swagger-resources/**", "/configuration/**", "/webjars/**");
}
}
And finally the actual configuration for the swagger using OpenApi:
package com.fujitsu.emom.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
/**
* Configuration for swagger using OpenApi.<br/>
* Notice the spring security must allow to access to the swagger ui at 'SecurityConfiguration.java'.<br/>
* There are also configuration at 'application.properties' for defining the URL to swagger page.
*/
#Configuration
public class SwaggerConfig {
public static final String SCHEME_NAME = "BearerScheme";
public static final String SCHEME = "Bearer";
#Bean
public OpenAPI customOpenAPI() {
var openApi = new OpenAPI().info(this.apiInfo());
this.addSecurity(openApi);
return openApi;
}
private Info apiInfo() {
var contact = new Contact();
contact.setEmail("mailbox#product.com");
contact.setName("product_admin");
contact.setUrl("http://product.com");
return new Info()
.title("Product API")
.description("Product description")
.termsOfService("http://product.com/terms_of_service")
.contact(contact)
// TODO: Version should be dynamically
.version("0.5.1");
}
private void addSecurity(OpenAPI openApi) {
var components = this.createComponents();
var securityItem = new SecurityRequirement().addList(SCHEME_NAME);
openApi.components(components).addSecurityItem(securityItem);
}
private Components createComponents() {
var components = new Components();
components.addSecuritySchemes(SCHEME_NAME, this.createSecurityScheme());
return components;
}
private SecurityScheme createSecurityScheme() {
return new SecurityScheme().name(SCHEME_NAME).type(SecurityScheme.Type.HTTP).scheme(SCHEME);
}
}
I have RESTful web service which I have developed in spring boot. I have integrated the swagger2 in my application using Gradle build tool.
testCompile('io.springfox:springfox-swagger2:2.6.1')
testCompile('io.springfox:springfox-swagger-ui:2.6.1')
I wrote the configuration file for swagger2 in following way
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select().apis(RequestHandlerSelectors.basePackage("com.example.restdemo.web"))
.paths(PathSelectors.any())
.build();
}
}
Now when I try to access the http://localhost:8080/v2/api-docs I am getting the JSON string. But when I am trying to access the http://localhost:8080/swagger-ui.html I am not getting Swagger UI view, I am getting the 406 error.
Did you try like this?
http://localhost:8080/swagger-ui.html#!/test-controller/
Here Controller class name is TestController
Also, replace
.select().apis(RequestHandlerSelectors.basePackage("com.example.restdemo.web"))
with
.select()
As below..
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
Can you try below Swagger configurations? basePackage is nothing but the entry point of your rest API layer. You can hardcode it in your program.
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
UiConfiguration uiConfig() {
return new UiConfiguration("validatorUrl", "list", "alpha", "schema",
UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, false, true, 60000L);
}
}
I have a spring boot application with jersey and gradle, and I am trying to automatically generate the API documentation using springfox.
I have followed the steps here: http://springfox.github.io/springfox/docs/current/
Here is what I did:
build.gradle:
dependencies {
.........
//Swagger
compile "io.springfox:springfox-swagger2:2.4.0"
compile "io.springfox:springfox-bean-validators:2.4.0"
compile 'io.springfox:springfox-swagger-ui:2.4.0'
}
Spring boot Application:
#SpringBootApplication
#EnableSwagger2
public class AnalyzerServiceApplication{
public static void main(String[] args) {
SpringApplication.run(AnalyzerServiceApplication.class, args);
}
#Bean
public Docket analyzerApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping("/")
.directModelSubstitute(LocalDate.class, String.class)
.genericModelSubstitutes(ResponseEntity.class)
.alternateTypeRules(
newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET,
newArrayList(new ResponseMessageBuilder()
.code(500)
.message("500 message")
.responseModel(new ModelRef("Error"))
.build()))
.securitySchemes(newArrayList(apiKey()))
.securityContexts(newArrayList(securityContext()))
.enableUrlTemplating(true)
.globalOperationParameters(
newArrayList(new ParameterBuilder()
.name("someGlobalParameter")
.description("Description of someGlobalParameter")
.modelRef(new ModelRef("string"))
.parameterType("query")
.required(true)
.build()))
.tags(new Tag("Pet Service", "All apis relating to pets"))
;
}
#Autowired
private TypeResolver typeResolver;
private ApiKey apiKey() {
return new ApiKey("mykey", "api_key", "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("/anyPath.*"))
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return newArrayList(
new SecurityReference("mykey", authorizationScopes));
}
#Bean
SecurityConfiguration security() {
return new SecurityConfiguration(
"test-app-client-id",
"test-app-client-secret",
"test-app-realm",
"test-app",
"apiKey",
ApiKeyVehicle.HEADER,
"api_key",
"," /*scope separator*/);
}
#Bean
UiConfiguration uiConfig() {
return new UiConfiguration("validatorUrl");
}
Now the controller (Jersey)
#Api(value = "/widget")
#Path("/widget")
#Component
public class WidgetController extends BaseController {
#Autowired
private WidgetService widgetService;
#GET
#Path("/secHealth")
#ApiOperation(value = "Find pet by ID", notes = "Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions", response = Pet.class)
#ApiResponses(value = { #ApiResponse(code = 400, message = "Invalid ID supplied"),
#ApiResponse(code = 404, message = "Pet not found") })
public Response getPet() {
//Do something
}
When I start the server and navigate to http://localhost:8080/swagger-ui.html, I can see the "green" UI screen with only the basic-error-controller listed there. My own controller is not there.
What did I do wrong?
Thanks
Guy
As of version 2.5.0 springfox only supports spring-mvc controllers. Jax-rs implementations like jersey aren't supported.
The current alternative to using springfox is to use the swagger-core library for jax-rs/jersey based services.
It does have the hooks needed to implement support for jersey in 2.6+. Here is an excerpt of a way to implement it in this issue
Currently ResourceConfig has a method called "getClasses" which will
list everything registerted. like Resources, Filters,etc... Maybe this
could help. But be aware that the returning classes could also be
filters or any other stuff you could register with jersey2.
To be able to see Jersey methods from Springfox swagger UI:
Configure your Swagger with Jersey following https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-Jersey-2.X-Project-Setup-1.5
Configure Springfox Swagger following http://springfox.github.io/springfox/docs/current/
Add in you SpringBoot application configuration class (annotated with #Configuration):
#Value("${springfox.documentation.swagger.v2.path}")
private String swagger2Endpoint;
In application.properties add reference to your Jersey swagger.json:
springfox.documentation.swagger.v2.path=/{change it to your Jersey api path}/swagger.json
Now you should be able to see Jersey Swagger generated api from Springfox Swagger UI page.
Thanks #Dilip-Krishnan for the springfox update and #Guy-Hudara for the question, I came up with the following solution to get swagger support in my springboot jersey powered app :
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/**
* As of version 2.5.0 springfox only supports spring-mvc controllers. Jax-rs implementations like jersey aren't supported.
*
* Fortunately io.swagger::swagger-jersey2-jaxrs::1.5.3 have the hooks needed to implement support for jersey in 2.6+.
*
* some pointers I used to get this swagger config done and swagger-core, springboot and jersey integrated:
* http://stackoverflow.com/questions/37640863/springfox-swagger-no-api-docs-with-spring-boot-jersey-and-gardle
* https://www.insaneprogramming.be/blog/2015/09/04/spring-jaxrs/
* https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-Jersey-2.X-Project-Setup-1.5#adding-the-dependencies-to-your-application
*
*/
#Configuration
public class SwaggerConfiguration {
#Autowired
ResourceConfig resourceConfig;
#PostConstruct
public void configure() {
resourceConfig.register(ApiListingResource.class);
resourceConfig.register(SwaggerSerializers.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8888");
beanConfig.setBasePath("/api");
beanConfig.setResourcePackage("com.my.resource");
beanConfig.setPrettyPrint(true);
beanConfig.setScan(true);
}
}
That worked out great for me
I am using spring-boot + jersey as restful implementation. I have setup the swagger and I am able to open the swagger ui on the browser. But the swagger-ui doesn't have any API to show, it is an empty page. Below is the code I setup for configuring swagger. How can I let swagger to scan my API definition in jersey?
SwaggerConfiguration.java
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.regex("/com.hello.*"))
.build().pathMapping("/swagger2");
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("App API")
.description("App API")
.version("1.0.0-SNAPSHOT")
.termsOfServiceUrl("")
.contact("Cooltoo company")
.license("Public")
.licenseUrl("http://hello.com/")
.build();
}
JerseyConfiguration.java
#Configuration
#EnableSwagger2
#EnableAutoConfiguration
#Api(value = "home", description = "Demo API")
#ApplicationPath("/nursego")
public class JerseyConfiguration extends ResourceConfig {
public JerseyConfiguration() {
register(BadgeAPI.class);
register(MultiPartFeature.class);
register(OrderAPI.class);
register(NurseAPI.class);
configureSwagger();
}
private void configureSwagger() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8080");
beanConfig.setBasePath("/nursego");
beanConfig.setResourcePackage("com.cooltoo.backend.api");
beanConfig.setPrettyPrint(true);
beanConfig.setScan(true);
register( io.swagger.jaxrs.listing.ApiListingResource.class );
register( io.swagger.jaxrs.listing.SwaggerSerializers.class );
}
}
When I open http://localhost:8080/swagger-ui.html, I see below image but none of them are from my API. I don't know where they are from
I used the BeanConfig class to embed the Swagger into my SpringBoot+Jersey implmentation, the code example is as follows,
#Component
#ApplicationPath( "/api" )
public class JerseyConfig extends ResourceConfig{
public JerseyConfig(){
// method for embedding the Swagger
configSwagger();
// registers the REST resource classes
configEndPoints();
}
private void configEndPoints(){
// here register all the REST resource classes
}
private void configSwagger(){
BeanConfig beanConfig = new BeanConfig();
beanConfig.setSchemes( new String[]{ "http" } );
beanConfig.setHost( "localhost:9001" );
beanConfig.setBasePath( "/api" );
beanConfig.setDescription( "REST API services for accessing the pcg application" );
beanConfig.setTitle( "RESTAPI" );
beanConfig.setVersion( "1.0.1" );
// this will tell Swagger config to scan only these packages
beanConfig.setResourcePackage( "com.aig.rest.web" );
beanConfig.setScan( true );
register( io.swagger.jaxrs.listing.ApiListingResource.class );
register( io.swagger.jaxrs.listing.SwaggerSerializers.class );
}
}
I believe #EnableSwagger2 annotation works if the endpoints are implemented using Spring MVC instead of Jersey (or any other JAX-RS impl).
I have detailed how to accomplish this in a blog post I created earlier this year, Microservices using Spring Boot, Jersey Swagger and Docker
Basically if you need to document your Jersey-implemented endpoints via Swagger, you would need to:
1)
Make sure your Spring Boot app scans for components located in specific packages (ie com.asimio.jerseyexample.config) via:
#SpringBootApplication(
scanBasePackages = {
"com.asimio.jerseyexample.config", "com.asimio.jerseyexample.rest"
}
)
2) Jersey configuration class implementation:
package com.asimio.jerseyexample.config;
...
#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();
}
private void registerEndpoints() {
this.register(HelloResource.class);
// Access through /<Jersey's servlet path>/application.wadl
this.register(WadlResource.class);
}
private void configureSwagger() {
// Available at localhost:port/swagger.json
this.register(ApiListingResource.class);
this.register(SwaggerSerializers.class);
BeanConfig config = new BeanConfig();
config.setConfigId("springboot-jersey-swagger-docker-example");
config.setTitle("Spring Boot + Jersey + Swagger + Docker Example");
config.setVersion("v1");
config.setContact("Orlando L Otero");
config.setSchemes(new String[] { "http", "https" });
config.setBasePath(this.apiPath);
config.setResourcePackage("com.asimio.jerseyexample.rest.v1");
config.setPrettyPrint(true);
config.setScan(true);
}
}
3) Resource implementation using JAX-RS (Jersey) and Swagger annotations:
package com.asimio.jerseyexample.rest.v1;
...
#Component
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Api(value = "Hello resource", produces = "application/json")
public class HelloResource {
private static final Logger LOGGER = LoggerFactory.getLogger(HelloResource.class);
#GET
#Path("v1/hello/{name}")
#ApiOperation(value = "Gets a hello resource. Version 1 - (version in URL)", response = Hello.class)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Hello resource found"),
#ApiResponse(code = 404, message = "Hello resource not found")
})
public Response getHelloVersionInUrl(#ApiParam #PathParam("name") String name) {
LOGGER.info("getHelloVersionInUrl() v1");
return this.getHello(name, "Version 1 - passed in URL");
}
...
}
4) Make sure your app's Spring Boot configuration file makes a distinction between Spring MVC (for actuator endpoints) and Jersey (for resources) endpoints:
application.yml
...
# Spring MVC dispatcher servlet path. Needs to be different than Jersey's to enable/disable Actuator endpoints access (/info, /health, ...)
server.servlet-path: /
# Jersey dispatcher servlet
spring.jersey.application-path: /api
...