Swagger-ui is incorrectly using a base path when you "Try Out" the api
See original GitHub issuehi @dilipkrish
Thanks for responding. I really appreciate it. Followed the instructions as suggested:
1- Removed dependencies springfox-swagger-ui, springfox-swagger2 and springfox-spring-webmvc 2- Added springfox-boot-starter
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
3- Removed @EnableSpringfoxWebMvc annotation from SwaggerConfig class.
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SwaggerConfig {
private static final Set<String> DEFAULT_PRODUCES_AND_CONSUMES = new HashSet<String>(
Arrays.asList("application/json", "application/xml"));
@Bean
public Docket apiDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
//.apis(RequestHandlerSelectors.any())
.apis(RequestHandlerSelectors.basePackage("com.github.abhinavmishra14"))
.paths(PathSelectors.any())
//Let's be specific to what we consumer and what we produce, instead of "*/*"
.build().consumes(DEFAULT_PRODUCES_AND_CONSUMES).produces(DEFAULT_PRODUCES_AND_CONSUMES)
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Restful webservices using spring boot")
.description("A demo restful webservice project using spring boot")
.version("1.0")
.license("Apache License Version 2.0")
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
.build();
}
}
4- Updated contextPath in application.properties :
server.servlet.contextPath=/rwsspringboot
5- Restarted the spring boot app.
I can see the api-docs at : http://127.0.0.1:8181/rwsspringboot/v2/api-docs
Where basePath is -> “/rwsspringboot” And all endpoints also shows context path in url, e.g.: /rwsspringboot/hello
Hence, when i load swagger-ui at : http://127.0.0.1:8181/rwsspringboot/swagger-ui/index.html , i see the UI loaded properly. However, when i use “Try-out” option, this is what i see:
CURL: curl -X GET “http://127.0.0.1:8181/rwsspringboot/rwsspringboot/hello” -H “accept: application/json” Requsest URL: http://127.0.0.1:8181/rwsspringboot/rwsspringboot/hello
Notice the path in the URL above ^^^^
Error:
Response { “timestamp”: “2020-06-27T18:35:49.913+00:00”, “status”: 404, “error”: “Not Found”, “message”: “No message available”, “path”: “/rwsspringboot/rwsspringboot/hello” }
So, the problem is that, as soon as i add context path, i start seeing the context path twice in the request urls. I think basepath should remain “/” or it should not append “/rwsspringboot” in the endpoint path when basepath is already set: /rwsspringboot
Full APIDocs:
{
swagger: "2.0",
info: {
description: "A demo restful webservice project using spring boot",
version: "1.0",
title: "Restful webservices using spring boot",
license: {
name: "Apache License Version 2.0",
url: "https://www.apache.org/licenses/LICENSE-2.0"
}
},
host: "127.0.0.1:8181",
basePath: "/",
tags: [
{
name: "hello-controller",
description: "Hello Controller"
},
{
name: "post-controller",
description: "Post Controller"
},
{
name: "user-rest-controller",
description: "User Rest Controller"
}
],
consumes: [
"application/json",
"application/xml"
],
produces: [
"application/json",
"application/xml"
],
paths: {
/hello: {
get: {
tags: [
"hello-controller"
],
summary: "hello",
operationId: "helloUsingGET",
responses: {
200: {
description: "OK",
schema: {
type: "string"
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
},
/sayHello: {
get: {
tags: [
"hello-controller"
],
summary: "hello",
operationId: "helloUsingGET_1",
parameters: [
{
name: "name",
in: "query",
description: "name",
required: true,
type: "string"
}
],
responses: {
200: {
description: "OK",
schema: {
type: "string"
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
},
/sayHelloAgain: {
get: {
tags: [
"hello-controller"
],
summary: "helloViaGetMapping",
operationId: "helloViaGetMappingUsingGET",
parameters: [
{
name: "name",
in: "query",
description: "name",
required: true,
type: "string"
}
],
responses: {
200: {
description: "OK",
schema: {
type: "string"
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
},
/sayHelloBean: {
get: {
tags: [
"hello-controller"
],
summary: "helloBeanViaGetMapping",
operationId: "helloBeanViaGetMappingUsingGET",
parameters: [
{
name: "name",
in: "query",
description: "name",
required: true,
type: "string"
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/Response"
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
},
/sayHelloBean/pathvariable/{name}: {
get: {
tags: [
"hello-controller"
],
summary: "helloBeanViaGetMappingPathVariable",
operationId: "helloBeanViaGetMappingPathVariableUsingGET",
parameters: [
{
name: "name",
in: "path",
description: "name",
required: true,
type: "string"
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/Response"
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
},
/users: {
get: {
tags: [
"user-rest-controller"
],
summary: "getAllUsers",
operationId: "getAllUsersUsingGET",
responses: {
200: {
description: "OK",
schema: {
type: "array",
items: {
$ref: "#/definitions/User"
}
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
},
post: {
tags: [
"user-rest-controller"
],
summary: "createUsers",
operationId: "createUsersUsingPOST",
parameters: [
{
in: "body",
name: "user",
description: "user",
required: true,
schema: {
$ref: "#/definitions/User"
}
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/Response"
}
},
201: {
description: "Created"
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
},
/users/{id}: {
get: {
tags: [
"user-rest-controller"
],
summary: "getUser",
operationId: "getUserUsingGET",
parameters: [
{
name: "id",
in: "path",
description: "id",
required: true,
type: "integer",
format: "int32"
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/EntityModel«User»"
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
},
delete: {
tags: [
"user-rest-controller"
],
summary: "deleteUser",
operationId: "deleteUserUsingDELETE",
parameters: [
{
name: "id",
in: "path",
description: "id",
required: true,
type: "integer",
format: "int32"
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/Response"
}
},
204: {
description: "No Content"
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
}
}
}
},
/users/{id}/posts: {
get: {
tags: [
"post-controller"
],
summary: "getAllPostsForAUser",
operationId: "getAllPostsForAUserUsingGET",
parameters: [
{
name: "id",
in: "path",
description: "id",
required: true,
type: "integer",
format: "int32"
}
],
responses: {
200: {
description: "OK",
schema: {
type: "array",
items: {
$ref: "#/definitions/Post"
}
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
},
post: {
tags: [
"post-controller"
],
summary: "createPost",
operationId: "createPostUsingPOST",
parameters: [
{
name: "id",
in: "path",
description: "id",
required: true,
type: "integer",
format: "int32"
},
{
in: "body",
name: "post",
description: "post",
required: true,
schema: {
$ref: "#/definitions/Post"
}
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/Response"
}
},
201: {
description: "Created"
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
},
/users/{id}/posts/{postId}: {
get: {
tags: [
"post-controller"
],
summary: "getPostDetails",
operationId: "getPostDetailsUsingGET",
parameters: [
{
name: "id",
in: "path",
description: "id",
required: true,
type: "integer",
format: "int32"
},
{
name: "postId",
in: "path",
description: "postId",
required: true,
type: "integer",
format: "int32"
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/EntityModel«Post»"
}
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}
}
},
definitions: {
EntityModel«Post»: {
type: "object",
properties: {
content: {
type: "string",
description: "Content must be at least 2 characters long",
minLength: 2,
maxLength: 2147483647
},
links: {
$ref: "#/definitions/Links"
}
},
title: "EntityModel«Post»"
},
EntityModel«User»: {
type: "object",
properties: {
birthdate: {
type: "string",
format: "date-time",
description: "Birthdate should be before current date"
},
links: {
$ref: "#/definitions/Links"
},
name: {
type: "string",
description: "Name must be at least 2 characters long",
minLength: 2,
maxLength: 2147483647
}
},
title: "EntityModel«User»"
},
Links: {
type: "object",
properties: {
empty: {
type: "boolean"
}
},
title: "Links"
},
Post: {
type: "object",
properties: {
content: {
type: "string",
description: "Content must be at least 2 characters long",
minLength: 2,
maxLength: 2147483647
}
},
title: "Post",
description: "Post description"
},
Response: {
type: "object",
properties: {
statusCode: {
type: "string"
},
statusMessage: {
type: "string"
}
},
title: "Response"
},
User: {
type: "object",
properties: {
birthdate: {
type: "string",
format: "date-time",
description: "Birthdate should be before current date"
},
name: {
type: "string",
description: "Name must be at least 2 characters long",
minLength: 2,
maxLength: 2147483647
}
},
title: "User",
description: "User description"
}
}
}```
**Here are all the dependencies in my project:**
```xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<!-- Dependency for swagger [Start]-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
<!-- Dependency for swagger [End]-->
<!-- For Hot Reloading -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>```
_Originally posted by @abhinavmishra14 in https://github.com/springfox/springfox/issues/3070#issuecomment-650601631_
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:13 (5 by maintainers)
Top GitHub Comments
@dilipkrish thanks for the fix. I just took latest snapshots after https://github.com/springfox/springfox/commit/03a10e6c4ac8a38212d435b3e9827753134a89a4 commit.
its working with classpath and duplicate context path is also gone. I can also see content type option. This is working with DocumentationType.SWAGGER_2.
Thanks alot for fixing the issue. Appreciate it.
This is good thanks for persisting the testing.