Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swagger-ui is incorrectly using a base path when you "Try Out" the api #3351

Closed
dilipkrish opened this issue Jun 27, 2020 · 13 comments
Closed
Labels
Milestone

Comments

@dilipkrish
Copy link
Member

hi @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_
@bravelionet
Copy link

It started three days ago

springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NoSuchMethodError: org.springframework.util.MultiValueMap.addIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)V

An attempt was made to call a method that does not exist. The attempt was made from the following location:

springfox.documentation.spring.web.scanners.ModelSpecificationRegistryBuilder.lambda$add$0(ModelSpecificationRegistryBuilder.java:37)

The following method did not exist:

org.springframework.util.MultiValueMap.addIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)V

@dilipkrish dilipkrish added this to the 3.0 milestone Jun 28, 2020
@abhinavmishra14
Copy link

hi @dilipkrish

Hope you are doing well, thanks for taking time to look into the issue. Appreciate it.

I took all latest snapshots after you closed the issue. To make sure snapshots are updated, i deleted all the local dependencies and took fresh update.

I am still seeing the same issue. I still see contextpath appended twice.

I noticed one more issue with content negotiation. I will open it as a separate issue.

Here is the API docs outpput example:

{
	"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": "/rwsspringboot",
	"tags": [
		{
			"name": "hello-controller",
			"description": "Hello Controller"
		}
	],
	"consumes": [
		"application/json",
		"application/xml"
	],
	"produces": [
		"application/json",
		"application/xml"
	],
	"paths": {
		"/rwsspringboot/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"
					}
				}
			}
		},
		"/rwsspringboot/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"
					}
				}
			}
		},
		"/rwsspringboot/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"
					}
				}
			}
		},
		"/rwsspringboot/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"
					}
				}
			}
		},
		"/rwsspringboot/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"
					}
				}
			}
		}
	},
	"definitions": {
		"Response": {
			"type": "object",
			"properties": {
				"statusCode": {
					"type": "string"
				},
				"statusMessage": {
					"type": "string"
				}
			},
			"title": "Response"
		}
	}
}

URLs, notice the context path appended twice:

curl -X GET "http://127.0.0.1:8181/rwsspringboot/rwsspringboot/hello" -H "accept: application/json"

http://127.0.0.1:8181/rwsspringboot/rwsspringboot/hello

Can you please open the issue again?

@dilipkrish
Copy link
Member Author

Can you try one of two things,

  • Force maven to redownload the snapshot
  • Force reload from origin, swagger ui and its resource. It has clearly been fixed if you look at the the demos

@abhinavmishra14
Copy link

abhinavmishra14 commented Jun 29, 2020

Can you try one of two things,

  • Force maven to redownload the snapshot
  • Force reload from origin, swagger ui and its resource. It has clearly been fixed if you look at the the demos

Hi @dilipkrish
thanks for respoding,

I tried all the suggestions you provided.

1- Used "mvn clean install -U" to force loading all the snapshot dependencies. I had done this before my last reply itself.
2- To make sure i have absolute latest dependencies, i deleted all the local depdencies inside "io" package, which also includes "io.springfox"
3- Clered the broswer cache and history, just in case.

I still see the same issue.

I tried downloading the demos project, the demo project is by default going to url: http://127.0.0.1:8181/springfox/swagger-ui.html which seems like the old URL and i get error page, swagger ui doesn't display. I downloaded master copy of this project.

This is what i see in the demo project:
public String home() {
return "redirect:swagger-ui.html";
}

Also http://127.0.0.1:8181/springfox/v2/api-docs doesn't load anything, i see error page

This was the context path in springfox-demos\boot-swagger demo project:

server.contextPath=/springfox

@dilipkrish
Copy link
Member Author

I believe there was an outage in jfrog which is why you're not seeing the latest snapshot

@abhinavmishra14
Copy link

I believe there was an outage in jfrog which is why you're not seeing the latest snapshot

Hi @dilipkrish
I was watching spring fox progress issue, i saw the issue about jfrog. When i tried the fix in the morning. Jfrog was working.

I tried again after your latest update, followed the same steps to bump the snapshot dependencies. Here is the list of snapshot dependencies under "io.springfox"

springfox-bean-validators : 3.0.0-20200629.203154-85
springfox-boot-starter : 3.0.0-20200629.203154-26
springfox-core : 3.0.0-20200629.205144-86
springfox-data-rest : 3.0.0-20200629.205144-85
springfox-oas : 3.0.0-20200629.205144-63
springfox-schema : 3.0.0-20200629.205145-85
springfox-spi : 3.0.0-20200629.203155-85
springfox-spring-web : 3.0.0-20200629.205146-84
springfox-spring-webflux : 3.0.0-20200629.205146-77
springfox-spring-webmvc : 3.0.0-20200629.205146-77
springfox-swagger-common : 3.0.0-20200629.205147-85
springfox-swagger-ui : 3.0.0-20200629.123819-84
springfox-swagger2 : 3.0.0-20200629.203157-85

I am still seeing the same issue in swagger-ui (/rwsspringboot/rwsspringboot/hello):

curl -X GET "http://127.0.0.1:8181/rwsspringboot/rwsspringboot/hello" -H "accept: application/json"

http://127.0.0.1:8181/rwsspringboot/rwsspringboot/hello

Api docs are still showing duplicate context paths, see the basepath and path:

{
	"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": "/rwsspringboot",
	"tags": [
		{
			"name": "hello-controller",
			"description": "Hello Controller"
		}
	],
	"consumes": [
		"application/json",
		"application/xml"
	],
	"produces": [
		"application/json",
		"application/xml"
	],
	"paths": {
		"/rwsspringboot/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"
					}
				}
			}
		}
	},
	"definitions": {
		"Response": {
			"type": "object",
			"properties": {
				"statusCode": {
					"type": "string"
				},
				"statusMessage": {
					"type": "string"
				}
			},
			"title": "Response"
		}
	}
}

I can attach the project link here if you want.

@dilipkrish
Copy link
Member Author

Could you change your docket to return new Docket(DocumentationType.OAS_30) and give it a try?

@abhinavmishra14
Copy link

Could you change your docket to return new Docket(DocumentationType.OAS_30) and give it a try?

Hi @dilipkrish

Thanks it worked with 'DocumentationType.OAS_30'. However, produces and consumes option is no longer available, it shows "/".

Do i need to make any changes to the docket config? See my docket api class below.

Api docs does show produces and consumes:

consumes: [
"application/json",
"application/xml"
],
produces: [
"application/json",
"application/xml"
],

Produces-consumes missing

Docket api 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.OAS_30)  
				.select()                                  
	            .apis(RequestHandlerSelectors.basePackage("com.github.abhinavmishra14.rws.controller.hello"))
				.paths(PathSelectors.any())
				.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();
	}
}

@dilipkrish
Copy link
Member Author

What does your hello request mapping look like?

@abhinavmishra14
Copy link

abhinavmishra14 commented Jun 29, 2020

What does your hello request mapping look like?

This is the rest controller class for /hello:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.github.abhinavmishra14.rws.model.Response;


@RestController
public class HelloController {
	
	@Autowired
	private MessageSource messageSource;

	@GetMapping(path = "/hello")
	public Response hello(@RequestParam final String name) {
		final Response response = new Response();
		response.setStatusCode("SUCCESS"); 
		response.setStatusMessage(messageSource.getMessage("hello.message", new Object[] {name}, LocaleContextHolder.getLocale()));
		return response;
	}
}

This is what i used to see with DocumentationType.SWAGGER_2:
Produces-consumes-old

Produces and consumes are defined in docket api class config, i have shared in last reply.

dilipkrish added a commit that referenced this issue Jun 30, 2020
@abhinavmishra14
Copy link

abhinavmishra14 commented Jun 30, 2020

@dilipkrish thanks for the fix. I just took latest snapshots after 03a10e6 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.

fixed

@abhinavmishra14
Copy link

hi @dilipkrish

I was doing some testing around the various http method. I came across another issue when i try to use "Try-Out" for a POST method implementation. I see below error. It doesn't allow to edit the request body content and also it doesn't allow to select the request content type. I see an error in browser console as well:

TryOutPost

This is the details of POST implementation method in API docs:

Path: /rwsspringboot/users/{id}/posts:

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 Details"
}
}
],
responses: {
200: {
description: "OK",
schema: {
$ref: "#/definitions/Response"
}
},
201: {
description: "Created"
},
401: {
description: "Unauthorized"
},
403: {
description: "Forbidden"
},
404: {
description: "Not Found"
}
}
}

Method in controller class:

       @PostMapping(path = "/users/{id}/posts")
	public ResponseEntity<Response> createPost(@PathVariable final int id, @RequestBody final Post post) {
		if (id > 0) {
			final User userById = userDao.findOne(id);
			if (userById == null) {
				throw new UserNotFoundException(String.format("User with id '%s' not found!", id));
			}
			final Post createdPost = postDao.save(id, post.getContent());
			final Response resp = new Response("CREATED", "Post created successfully.");
			resp.setAdditionalProperty("post", createdPost);
			final URI location = ServletUriComponentsBuilder.fromCurrentRequest().buildAndExpand(id).toUri();
			return ResponseEntity.created(location).body(resp);
		} else {
			throw new RWSException(String.format("User id '%s' is invalid!", id));
		}
	}

Let me know if you need any details. Thanks again for all the help resolving the issues.

@dilipkrish
Copy link
Member Author

This is good thanks for persisting the testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants