Not able to get Swagger UI with spring boot - spring-boot

I am following the article at http://raibledesigns.com/rd/entry/documenting_your_spring_api_with.
Everything works fine but not able to integrate Swagger UI.
http://localhost:8080/docs/index.html
results in /error redirection.

I know this is an old question but maybe this will help someone with the similar problem in the future.
I followed similar tutorial to the one you mentioned and I made it work without a problem. I put my own document on how to setup a Swagger with UI in a Spring boot project couple of weeks ago. Maybe it will help you out since it is shorter and more up-to-date.
Add Maven dependencies
Stick these in your pom.xml:
<dependency>
<groupId>com.mangofactory</groupId>
<artifactId>swagger-springmvc</artifactId>
<version>1.0.2</version>
<type>jar</type>
</dependency>
Add Swagger UI
Download the Swagger UI from github. Copy the dist folder into your webapp directory and rename dist to swagger (or any name you like).
Open the index.html file inside the copied directory and change the url in the first javascript function so it points to the /api-docs endpoint:
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "/project-name/api-docs";
}
Configure Swagger
Create a SwaggerConfig.java class and configure swagger there:
#Configuration
#EnableSwagger
#EnableAutoConfiguration
public class SwaggerConfig {
private SpringSwaggerConfig springSwaggerConfig;
#Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
this.springSwaggerConfig = springSwaggerConfig;
}
#Bean
public SwaggerSpringMvcPlugin customImplementation() {
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
// Root level documentation
.apiInfo(new ApiInfo("Swagger-demo JSON API", "This service provides a JSON representation the service API", null, null, null, null))
.useDefaultResponseMessages(false)
// Map the specific URL patterns into Swagger
.includePatterns("/greeting.*");
}
}
Your swagger should be up and running now. Try accessing /project-name/swagger/index.html.

I'm answering this with swagger2 configuration inside a gradle based spring boot application. Following are the configuration required for Swagger2.
Add Gradle Configuartion
Add Gradle dependencies inside build.gradle file
dependencies {
compile("io.springfox:springfox-swagger2:2.0.2")
compile("io.springfox:springfox-swagger-ui:2.0.2")
}
Swagger2 Confugration Class
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket userApi() {
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()))
.apiInfo(apiInfo());
}
#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("123456", "test-app-realm", "clientapp", "apiKey");
}
#Bean
UiConfiguration uiConfig() {
return new UiConfiguration("validatorUrl");
}
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo("DSM API", "API for DSM", "1.0.0", "termsOfServiceUrl",
"nijo#ndimensionz.com", null, null);
return apiInfo;
}
}
Add Swagger UI
Download the Swagger UI from github. Copy the dist folder into your src/main/resources/static directory and rename dist to swagger
HomeController.class
#Api(basePath = "/", value = "/", description = "Home Controller")
#Controller
public class HomeController {
#RequestMapping("/")
public String home() {
return "redirect:swagger-ui.html";
}
}
MyApplication.class
#SpringBootApplication
#ComponentScan(basePackageClasses = SwaggerConfig.class)
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Configure your spring boot application with dependencies and run to see the API.
the url will be http://localhost:8080/v2/swagger-ui.html
you can also customize this as above answer.

I too face same problem. I was able to see json but not swagger ui after adding dest folder aslo. In initialize class which extends SpringBootServletInitializer I added below method then it was working perfect
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SampleWebStaticApplication.class);
}
please refer link

Here is my working configuration incase it helps anyone else
#Configuration
#EnableSwagger2
#Profile({"!production"})
public class SwaggerConfiguration extends WebMvcConfigurerAdapter {
#Autowired
private ServletContext servletContext;
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.host("localhost")
.directModelSubstitute(LocalDate.class, Date.class)
.pathProvider(new RelativePathProvider(servletContext) {
#Override
public String getApplicationBasePath() {
return "/docs";
}
})
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
Since I am also using Spring security, I also had to add the following to my whitelist:
private String[] swaggerWhiteList = {"/v2/api-docs", "/swagger-resources/configuration/ui", "/swagger-resources", "/swagger-resources/configuration/security", "/swagger-ui.html", "/swagger.json", "/webjars/**"};
Then I could access it using the following url:
http://localhost:8080/docs/swagger-ui.html

Related

required a bean of type 'org.springframework.cloud.netflix.ribbon.SpringClientFactory' that could not be found

I have this test project which I would like to migrate to more recent version:
#Configuration
public class LoadbalancerConfig extends RibbonLoadBalancerClient {
public LoadbalancerConfig(SpringClientFactory clientFactory) {
super(clientFactory);
}
}
Full code example: https://github.com/rcbandit111/Generic_SO_POC/blob/master/src/main/java/org/merchant/database/service/sql/LoadbalancerConfig.java
Do you know how I can migrate this code to latest load balancer version?
I think examining the RibbonAutoConfiguration class gives you a good hint of how you should configure things.
First remove #Configuration from LoadbalancerConfig, I also renamed LoadbalancerConfig to CustomLoadbalancer to prevent confusion.
public class CustomLoadbalancer extends RibbonLoadBalancerClient {
public CustomLoadbalancer(SpringClientFactory clientFactory) {
super(clientFactory);
}
}
add the following dependency to your gradle
com.netflix.ribbon:ribbon:2.7.18
then add a configuration class like:
#Configuration
#ConditionalOnClass({Ribbon.class})
#AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
#ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
havingValue = "true", matchIfMissing = true)
#AutoConfigureBefore(LoadBalancerAutoConfiguration.class)
public class LoadBalancerClientConfig {
#Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
#Bean
public CustomLoadbalancer customLoadbalancer() {
return new CustomLoadbalancer(springClientFactory());
}
#Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
}
If you want to use Spring cloud load balancer instead of above configuration add spring-cloud-starter-loadbalancer dependency to your gradle.build and for configuration you only need this bean:
#LoadBalanced
#Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
}
This RestTemplate pretty works identical to standard RestTemplate class, except instead of using physical location of the service, you need to build the URL using Eureka service ID.
Here is an example of how could you possibly use it in your code
#Component
public class LoadBalancedTemplateClient {
#Autowired
RestTemplate restTemplate;
#Autowired
User user;
public ResponseEntity<Result> getResult() {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder
.fromHttpUrl("http://application-id/v1/") // application id registered in eureka
.queryParam("id", user.getUserId());
return restTemplate.getForEntity(uriComponentsBuilder.toUriString(),
Result.class);
}
}
Also if you wish to use reactive client the process is the same first define the bean:
#LoadBalanced
#Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
and then inject and use it when you need:
#Autowired
private WebClient.Builder webClient;
public Mono<String> doSomething() {
return webClient
.build()
.get()
.uri("http://application-id/v1/")
.retrieve()
.bodyToMono(String.class);
}
Also you can check documentation for additional information: documentation

Add context path to requests in swagger

I have an eureka service which has a swagger. The eureka is on http://localhost:8050
and the service goes by name /service. The issue is that when i open swagger and try to make a request, it sends it to http://localhost:8050/service/somecontroller. The service has a context path "path" so it should be http://localhost:8050/service/path/somecontroller. This is the configuration of the swagger:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.test")).paths(PathSelectors.any())
.build();
}
Springfox has an open issue (#2817) for your case, you can try one of the workarounds proposed by some users there.
Managed to change the context path of the swagger like this:
#Value("${contextPath}")
private String contextPath;
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
//.host(retrieveHostHostname())
.pathProvider(new PathProvider() {
#Override
public String getApplicationBasePath() {
return contextPath;
}
#Override
public String getOperationPath(String s) {
return s.replace("somecontroller", contextPath+"/somecontroller");
}
#Override
public String getResourceListingPath(String s, String s1) {
return "/";
}
}).select()
.apis(RequestHandlerSelectors.basePackage("com.test")).paths(PathSelectors.any())
.build();
}

springboot Multiple Dockets with the same group name are not supported

I have the follwoing spring boot configuration for swagger, when service starts up i get the following error, im not sure exactly why this would be the case. I have followed a tutorial and it works for them.
java.lang.IllegalStateException: Multiple Dockets with the same group name are not supported. The following duplicate groups were discovered. default
#Configuration
#EnableSwagger2
#Import(BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig {
#Bean
public Docket apiDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("test.rest"))
.paths(PathSelectors.ant("/test/**"))
.build()
.apiInfo(apiInfo());
}
// Describe the apis
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("test")
.description("Test")
.version("1.0.0")
.license("vvv")
.build();
}
}
i also have another config
#OurApp
#EnableSwagger2
public class CoreApp extends OurApp {
}
Here you are trying to do multiple Dockets with the same group name, which is not acceptable. Please review the link provided.
groupName(java.lang.String groupName) If more than one instance of
Docket exists, each one must have a unique groupName as supplied by
this method. Documentation
public class Docket implements DocumentationPlugin {
public static final String DEFAULT_GROUP_NAME = "default";
}
You can see above DocumentPlugin has groupname as "default".
public Docket(DocumentationType documentationType) {
this.apiInfo = ApiInfo.DEFAULT;
this.groupName = "default";
And above has "default" as group name.
So, you need to have two different group names for both the Dockets.
All you need to do is change your code as below: overwrite the existing default groupname.
#Configuration
#EnableSwagger2
#Import(BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig {
#Bean
public Docket apiDocket() {
String groupName = "Swagger";
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("test.rest"))
.paths(PathSelectors.ant("/test/**"))
.build()
.groupName(groupName)
.apiInfo(apiInfo());
}
// Describe the apis
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("test")
.description("Test")
.version("1.0.0")
.license("vvv")
.build();
}
}
Seems like you have given the same group name which is not accepted, please check and set the group name properly.

Conflict between Swagger2 and jackson-datatype-jsr310 in Spring Boot application

I am creating a REST API using Spring Boot and have issues serialising LocalDateTime when I use Swagger 2.
Without Swagger the JSON output is like this:
{
"id": 1,
...
"creationTimestamp": "2018-08-01T15:39:09.819"
}
And with Swagger it is like this:
{
"id": 1,
...
"creationTimestamp": [
2018,
8,
1,
15,
40,
59,
438000000
]
}
I have added this to the pom file so the dates are serialised correctly:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
This is the configuration of Jackson:
#Configuration
public class JacksonConfiguration {
#Bean
#Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
}
And this is the configuration of Swagger:
#Configuration
#EnableSwagger2
public class SwaggerConfiguration extends WebMvcConfigurationSupport {
#Bean
public Docket messageApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.xxx.message.controller"))
.build()
.apiInfo(metaData());
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title("Message service")
.version("1.0.0")
.build();
}
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
It works when I add add like this a deserialiser to the fields of the DTO. However it should work without having to add it.
#JsonFormat(pattern = "dd/MM/yyyy")
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime creationTimestamp;
I guess that the problem is that Swagger has its own object mapper that is overriding the other. Any idea of how to solve it?
Thanks in advance
As I see, the problem occurs when SwaggerConfiguration extends WebMvcConfigurationSupport. You can remove this extension if you don't need it.

Swagger doesn't display information about methods - SpringBoot

I have an API in Java SpringBoot and I want to document it in Swagger.
I have done the following (I only include classes that contain some code related to Swagger):
Main class
#EnableSwagger2
public class ProvisioningApiApplication {
public static void main(String[] args) {
if (AuthConfigFactory.getFactory() == null) {
AuthConfigFactory.setFactory(new AuthConfigFactoryImpl());
}
SpringApplication.run(ProvisioningApiApplication.class, args);
}
#Bean
public Docket swaggerSpringMvcPluggin() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.select()
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
#Component
#Primary
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
setSerializationInclusion(JsonInclude.Include.NON_NULL);
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
enable(SerializationFeature.INDENT_OUTPUT);
}
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Provisioning API")
.version("0.0.1")
.build();
}
}
Controller
#RestController
#EnableAutoConfiguration
#CrossOrigin
public class RecursoController {
#Autowired
private Configuration configuration;
#Autowired
private TypeSpecService typeSpecService;
#Autowired
private IoTAgentService ioTAgentService;
#Autowired
private OrionService orionService;
#Autowired
private DeviceIdService deviceIdService;
#ApiOperation(value = "Put a device", nickname = "provisionDevice", tags = "Device")
#ApiResponses({
#ApiResponse(code = 200, message = "Ok", response = NewDeviceResponse.class)
})
#RequestMapping(method = RequestMethod.PUT, value = "/devices", consumes = "application/json", produces = "application/json")
public ResponseEntity<NewDeviceResponse> provisionDevice(#RequestBody NewDeviceRequest newDeviceRequest,
#RequestHeader("X-Auth-Token") String oAuthToken) {
// what my method does
}
The documentation results in the following swagger.json file:
{
swagger: "2.0",
info: {
version: "0.0.1",
title: "Provisioning API"
},
host: "localhost:8080",
basePath: "/"
}
As you can see, it only contains the name and the version of API but not the provisionDevice method.
I've tried everything but I can't figure it out what I'm doing bad. What am I missing?
Did you add #Api annotation in your class, where you have your main services?

Resources