Swagger 3 with Springboot: Unable to infer base url - spring-boot

I use Springboot with swagger 3:
<!-- SWAGGER -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
I use a default /api prefix to all my endpoints.
This is how I configured my SwaggerConfig:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public static final String AUTHORIZATION_HEADER = "Authorization";
#Bean
public Docket api() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.securityContexts(Collections.singletonList(securityContext()))
.securitySchemes(Collections.singletonList(apiKey()))
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build().pathMapping("/api");
return docket;
}
private ApiKey apiKey() {
return new ApiKey("JWT", AUTHORIZATION_HEADER, "header");
}
// ......
}
When I try to access to my swagger-ui http://myhost/swagger-ui I get a popup with this message Unable to infer base url. This is common when using dynamic servlet registration or when the API is behind an API Gateway. The base url is the root of where all the swagger resources are served. For e.g. if the api is available at http://example.org/api/v2/api-docs then the base url is http://example.org/api/. Please enter the location manually: asking me to define the location with.
When I enter my prefix manually : http://myhost/api then every thing is fine.
Any idea how to define my REST API prefix ?

Related

How do I hide endpoints only for a specific profile in spring boot?

I am using open api 3 and want to hide some endpoints in swagger ui. In swagger2 I found what can be done in this way by creating my own annotation, but I don't understand how I can do it in openapi3.
#Bean
public Docket postsApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("public-api")
.apiInfo(apiInfo())
.select()
// This is the part that will ignore the method
.apis((handler) -> !handler.isAnnotatedWith(IgnoreForProd.class))
.build();
}
I've solved this issue like this:
1st - I have removed the springfox and springfox-swagger-ui dependencies from pom.xml and replaced it with springdoc-openapi-ui like this:
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.6</version>
2nd - created the Bean below:
#Configuration
public class OpenAPIConfig {
#Value("${version:unknown}")
private String version;
#Bean
public OpenAPI springShopOpenAPI() {
return new OpenAPI()
.info(new Info().title("Title API")
.description("Description API")
.version(version)
.license(new License().name("Apache 2.0").url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("SpringShop Wiki Documentation")
.url("https://springshop.wiki.github.org/docs"));
}
}
3rd - then on application.yml you can filter by package and/or path:
springdoc:
packages-to-scan: com.company.projectxyz.controller
pathsToMatch: /api/whatever/public/**
4th - and if you need to filter different paths/packages per profile, also on application.yml you can do the 3rd step inside of:
spring.config.activate.on-profile: dev/qa/prod...
and access your swagger at /swagger-ui/index.html
I hope it helps =)

Adding base path to swagger documentaion

I am trying to change the base path of swagger codumentation. Currently I have
#RequestMapping(path = "/api/resourceName/v1")
and swagger config
return new Docket(DocumentationType.SWAGGER_2).
select()
.apis(RequestHandlerSelectors.basePackage("com.company"))
.paths(PathSelectors.ant("/api/**"))
.build()
.apiInfo(apiInfo());
This is giving swagger base path as "basePath": "/"
I want to add base path as "basePath": "/api" so I followed diff threads like this How to change basePath for Springfox Swagger 2.0 and added
return new Docket(DocumentationType.SWAGGER_2).
select()
.apis(RequestHandlerSelectors.basePackage("com.company"))
.paths(PathSelectors.ant("/api/**"))
.build()
.apiInfo(apiInfo())
.pathProvider(new RelativePathProvider(servletContext) {
#Override
public String getApplicationBasePath() {
return "/api";
}
});
now the base path is changed to "basePath": "/api" and I updated my path mapping to #RequestMapping(path = "/resourceName/v1") as base has been added.
When I send the request from swagger, the request is going to /api/resourceName/v1 but the service returns 404.
When I send the request through postman for /resourceName.v1 then it works.
So the api is registred as /resourceName/v1 and the base is just added by swagger on top of it and will not work if the request sent throguh swagger UI
Then I added server.servlet-path=/api to application.properties to register basepath in the request mapping and now swagger shows the base path as /api without needing additional configuration.
But the problem is now swagger documentation is available at http://localhost:8080/api/swagger-ui.html instead of http://localhost:8080/swagger-ui.html. As we have all our other services doc at http://host/swagger-ui.html this is not useful.
Is there any way to add the base and still access the doc at http://host/swagger-ui.html and api's works as expected fro swagger and postman
Yes, you can add base path for all swagger requests. I used following config for this purpose:
#Configuration
#EnableSwagger2
public class SpringFoxConfig {
#Bean
public Docket api() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
docket.pathMapping("api");
return docket;
}
}
Hope this helps.
There is way to change the api-doc base path using properties defined in application.properties file.
The properties are:
springfox.documentation.openApi.v3.path=/external/api/app/v3/api-docs
springfox.documentation.swaggerv2.path=/external/api/app/v2/api-docs
This can help you to change the path.

Spring Integration with swagger: How can I implement it?

Cant expose spring integration flow apis, with swagger documentation
I have some apis exposed using spring integration. We tried document it, with springfox dependency (swagger2).
But when access to: http://localhost:8080/myProject/swagger-ui.html, the page is empty, we cant see the services with swagger format
My example;
Class definition:
#Configuration
#EnableSwagger2
public class ConsultaBdnFlow {
....
}
Swagger configuration:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
Swagger Dependencies:
<!-- Start Swagger 2 with SpringFox -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.3.0</version>
</dependency>
<!-- End Swagger 2 with SpringFox -->
Flow to expose the service:
#Bean
public IntegrationFlow bdnBlacklistFlow() {
return IntegrationFlows
.from(Http.inboundGateway("/consultas/bdn")
.requestPayloadType(String.class)
.requestChannel(requestBlacklistChannel())
.replyChannel(replyBlacklistChannel())
)
.get();
}
When run the project we can access to http://localhost:8080/swagger-ui.html, but dont see the service swagger document
Maybe you are missing VendorExtension.
Change your Docket Bean to something like this and see what happens. Also include the errors it is giving you if it doesn't work. This worked on Swagger dependencies (2.9.2) and Spring-boot 2.2.0.M2.
#Bean
public Docket apiDocket() {
Contact contact = new Contact(
"You name",
"Your webesite",
"Your email"
);
List<VendorExtension> vendorExtensions = new ArrayList<>();
ApiInfo apiInfo = new ApiInfo(
"RESTful API documentation",
"This pages documents Turing Ecommerce RESTful API endpoints", "1.0",
"Website", contact,
"Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0",vendorExtensions);
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.company.package"))
.paths(PathSelectors.any())
.build();
return docket;
}

Spring Boot + Swagger-Ui yml generate

Create a swagger-ui with the yaml file generated by swagger-editor
Creating an auto swagger-ui using Annotation was successful.
But I do not know how to create Swagger-ui with yaml created with swagger-editor. Is there anyone who can explain in detail? The development environment uses SpringBoot 2.0.4.
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket getApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(paths())
.build();
}
private Predicate<String> paths() {
return Predicates.and(
PathSelectors.regex("/shop.*"),
Predicates.not(PathSelectors.regex("/error.*")));
}
}
The above code can make all the APIs inside the server Swagger-ui.
However, I would like to configure swagger-ui with the yaml file that I wrote myself through the swagger-editor.

Spring Boot & Swagger UI. Set JWT token

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);
}
}

Resources