Skip to content

Springfox integration issue for Swagger2 with Spring boot application #783

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

Closed
dpatra1 opened this issue Jun 9, 2015 · 43 comments
Closed
Labels

Comments

@dpatra1
Copy link

dpatra1 commented Jun 9, 2015

Hi

I am new to Springfix and Swagger2 as well. So i am in a project where all the latest technologies are used like: java8, Spring boot etc and we wanted to integrate Swagger2 with Springfox to generate the APi documentation.

I have gone through the 'http://springfox.github.io/springfox/docs/snapshot/' page but i am still having issue and i dont see the Swagger Ui api documentation page .

Its giving me an error in the tomcat server when i am trying to hit:
'http://localhost:8081/springfox/'
WARN 6872 --- [nio-8081-exec-1] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/springfox/] in DispatcherServlet with name 'dispatcherServlet'

I have a feeling that i am still missing some configuration w.r.t the static resources which is causing this issue.

Can you please help me here ?

Here are my dependencies and configurations.

io.springfox
springfox-swagger2
2.0.1


org.webjars
swagger-ui
2.1.8-M1

Swagger2 java configuration:

package com.diginsite.microservices;

import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;

import java.util.List;

import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import com.fasterxml.classmate.TypeResolver;

@EnableSwagger2
@Configuration
@EnableWebMvc
@ComponentScan("com.diginsite.microservices.web")
public class Swagger2SpringBoot {
@Autowired
private TypeResolver typeResolver;

@Bean
public Docket swaggerSpringMvcPlugin() {
    return new Docket(DocumentationType.SWAGGER_2)
            .groupName("v1-bbs")
            .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()));
}

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

}

How i am testing: I am running my MicroService using spring boot and trying to hit the above URL.

Thanks,
Deba

@dilipkrish
Copy link
Member

what do you get when you hit `http://localhost:8081/springfox/swagger-ui.html'?

@dpatra1
Copy link
Author

dpatra1 commented Jun 9, 2015

I see the same error as i explained above:

2015-06-09 13:22:02.974 WARN 9715 --- [nio-8081-exec-1] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/springfox/swagger-ui.html] in DispatcherServlet with name 'dispatcherServlet'

Also i see that common error page in the browser as well.

Do i need to add any static content to resources folder ? Or i am missing any configuration w.r.t springfox and swagger ?

@dilipkrish
Copy link
Member

My bad could you check http://localhost:8081/swagger-ui.html

@dpatra1
Copy link
Author

dpatra1 commented Jun 9, 2015

i tried the above URL as well and the same error i see for that page:
2015-06-09 13:40:21.193 WARN 9715 --- [nio-8081-exec-2] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/swagger-ui.html] in DispatcherServlet with name 'dispatcherServlet'

@dpatra1
Copy link
Author

dpatra1 commented Jun 9, 2015

Am i missing any configurations related to springfox/Swagger2 ?

Also do i need to copy any static content to resource folder ?

FYI, i have already copied the dist folder contents under ///src/main/resources/static/swagger

Any help is greatly appriciated

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

Do i need to configure this ?

package springfoxdemo.java.swagger;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.petstore.controller.PetController;

@EnableWebMvc
@ComponentScan(basePackageClasses = PetController.class)
@Import(SwaggerConfiguration.class)
public class SpringConfig extends WebMvcConfigurerAdapter {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("swagger-ui.html")
            .addResourceLocations("classpath:/META-INF/resources/");

    registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/");
}

}

Because i have seen this in the springfox demo project but i am not sure whether this is required for me to configure or not

@dilipkrish
Copy link
Member

Only if you are not using spring-boot

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

So in that case, i am using spring boot so i dont need this configuration.

Any other clue ?

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

What about

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");

    registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/");
}

is this something i need to configure ?

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

@dilipkrish - Could you please help me setting up springfox with spring boot ?

@dilipkrish
Copy link
Member

Have you looked at https://github.com/springfox/springfox-demos? It has a complete boot-swaggerexample

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

Its not maven supported otherwise i could have build it on my local and test it....But still let me take a look at the configuration files atleast

@dilipkrish
Copy link
Member

There is nothing maven or gradle specific in the build, other than how you launch the application like tomcat/boot plugins.

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

@dilipkrish - I am new to both springfox as well as spring-boot, so its taking sometime for me to understand the integration.

But now i have looked at the demo application and i have setup my application exactly same way but i am still seeing the same error which i have posted above.

Here is my Spring boot MicroService:

package com.diginsite.microservices;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

@SpringBootApplication
public class BusinessBankingService extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(BusinessBankingService.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(BusinessBankingService.class, args);
    }
}

Here is my WebMvcConfiguration.java:

package com.diginsite.microservices;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2CollectionHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.oxm.xstream.XStreamMarshaller;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.springframework.web.servlet.view.xml.MarshallingView;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Configuration
@Import(Swagger2SpringBoot.class)
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {

    private static final String contextPath="com.intuit.schema.domain.banking.account.v2:"+
            "com.intuit.schema.domain.banking.accounttransaction.v2:" +
            "com.intuit.schema.domain.banking.image.v2:" +
            "com.intuit.schema.domain.banking.ficustomer.v2:" +
            "com.intuit.schema.domain.banking.financialinfo.v2:" +
            "com.intuit.schema.domain.banking.financialinstitution.v2:" +
            "com.intuit.schema.domain.banking.fundingaccount.v2:" +
            "com.intuit.schema.domain.banking.location.v2:" +
            "com.intuit.schema.domain.banking.scheduledtransfer.v2:" +
            "com.intuit.schema.domain.banking.transfer.v2:" +
            "com.intuit.schema.domain.billpay.billpayment.v2:" +
            "com.intuit.schema.domain.billpay.payee.v2:" +
            "com.intuit.schema.fs.common.v2:" +
            "com.intuit.schema.domain.banking.configuration.v2:" +
            "com.intuit.schema.domain.banking.auditdata.v1:"+
            "com.intuit.schema.fs.alternatecredentials.v2:" +
            "com.intuit.schema.domain.banking.businessbanking.v1"
            ;

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_JSON);
        configurer.defaultContentTypeStrategy(webRequest -> {
            if(webRequest.getHeader("Content-Type") != null) {
                switch(webRequest.getHeader("Content-Type")) {
                    case MediaType.APPLICATION_JSON_VALUE:
                        return Arrays.asList(MediaType.APPLICATION_JSON);
                    case MediaType.APPLICATION_XML_VALUE:
                        return Arrays.asList(MediaType.APPLICATION_XML);
                    default:
                        return Arrays.asList(MediaType.APPLICATION_JSON);
                }
            } else {
                return Arrays.asList(MediaType.APPLICATION_JSON);
            }
        });
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();

        MarshallingHttpMessageConverter  xmlConverter = marshallingHttpMessageConverter();
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        xmlConverter.setMarshaller(jaxb2Marshaller);
        xmlConverter.setUnmarshaller(jaxb2Marshaller);

        converters.add(xmlConverter);

        converters.add(jaxb2CollectionHttpMessageConverter());
        converters.add(jaxb2RootElementHttpMessageConverter());

        stringConverter.setWriteAcceptCharset(false);

        converters.add(new ByteArrayHttpMessageConverter());
        converters.add(stringConverter);
        converters.add(new ResourceHttpMessageConverter());

        converters.add(new SourceHttpMessageConverter<>());
        converters.add(new AllEncompassingFormHttpMessageConverter());
        converters.add(jackson2Converter());
    }

    @Bean
    public MarshallingHttpMessageConverter marshallingHttpMessageConverter() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setContextPath(contextPath);
        jaxb2Marshaller.setPackagesToScan("com.intuit.schema.domain.banking.businessbanking.v1");
        jaxb2Marshaller.setCheckForXmlRootElement(false);
        jaxb2Marshaller.setSupportJaxbElementClass(true);

        MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter();

        converter.setMarshaller(jaxb2Marshaller);
        converter.setUnmarshaller(jaxb2Marshaller);
        converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));

        return converter;
    }

    @Bean
    public ContentNegotiatingViewResolver contentNegotiatingViewResolver() {
        ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
        MarshallingView xmlView = new MarshallingView();
        XStreamMarshaller xstreamMarshaller = new XStreamMarshaller();
        xstreamMarshaller.setAutodetectAnnotations(true);
        xmlView.setMarshaller(xstreamMarshaller);
        xmlView.setContentType("application/xml");

        MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
        jsonView.setContentType("application/json");
        jsonView.setDisableCaching(false);

        contentNegotiatingViewResolver.setDefaultViews(Arrays.asList(xmlView, jsonView));

        ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(webRequest -> {
            if(webRequest.getHeader("Content-Type") != null) {
                switch(webRequest.getHeader("Content-Type")) {
                    case MediaType.APPLICATION_JSON_VALUE:
                        return Arrays.asList(MediaType.APPLICATION_JSON);
                    case MediaType.APPLICATION_XML_VALUE:
                        return Arrays.asList(MediaType.APPLICATION_XML);
                    default:
                        return Arrays.asList(MediaType.APPLICATION_JSON);
                }
            } else {
                return Arrays.asList(MediaType.APPLICATION_JSON);
            }
        });

        contentNegotiatingViewResolver.setContentNegotiationManager(contentNegotiationManager);
        Map<String, String> mediaTypes = new HashMap<>();
        mediaTypes.put("xml", "application/xml");
        mediaTypes.put("json", "application/json");
        contentNegotiatingViewResolver.setMediaTypes(mediaTypes);

        return contentNegotiatingViewResolver;
    }

    @Bean
    public Jaxb2CollectionHttpMessageConverter jaxb2CollectionHttpMessageConverter() {
        Jaxb2CollectionHttpMessageConverter converter = new Jaxb2CollectionHttpMessageConverter();
        converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));
        return converter;
    }

    @Bean
    public Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter() {
        Jaxb2RootElementHttpMessageConverter converter = new Jaxb2RootElementHttpMessageConverter();
        converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_XML));
        return converter;
    }

    @Bean
    public MappingJackson2HttpMessageConverter jackson2Converter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper());
        return converter;
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        return objectMapper;
    }

//    @Override
//    public void addResourceHandlers(ResourceHandlerRegistry registry) {
//        registry.addResourceHandler("swagger-ui.html")
//                .addResourceLocations("classpath:/META-INF/resources/");
//
//        registry.addResourceHandler("/webjars/**")
//                .addResourceLocations("classpath:/META-INF/resources/webjars/");
//    }

}

Here is my Swagger Configuration java file:

package com.diginsite.microservices;

import static com.google.common.base.Predicates.or;
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.builders.PathSelectors.regex;
import static springfox.documentation.schema.AlternateTypeRules.newRule;

import java.util.List;

import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Predicate;

@SpringBootApplication
@EnableSwagger2
@Configuration
@EnableWebMvc
@ComponentScan("com.diginsite.microservices.web")
public class Swagger2SpringBoot {
    static final String detailDescription = "The `Business Banking Microservice` is a RESTful API that provides PD teams with out of the box functionality for Digital Insight's `Business Banking` suite of products. \n \n"
            +"Below is a list of available REST API calls for business banking resources.";
    @Autowired
    private TypeResolver typeResolver;

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Swagger2SpringBoot.class, args);
    }

    @Bean
    public Docket swaggerSpringMvcPlugin() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("v1-bbs")
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .pathMapping("/v1")
                .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()));
    }

    private Predicate<String> entitlementsAPIPaths() {
        return or(
                regex("/v1/fis/{fiId}/businessCustomers.*"),
                regex("/v1/fis/{fiId}/companies/{companyId}/entitlements.*")
        );
    }

    private ApiKey apiKey() {
        return new ApiKey("mykey", "api_key", "header");
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Overview")
                .description(detailDescription)
                .termsOfServiceUrl("http://springfox.io")
                .contact("springfox")
                .license("Apache License Version 2.0")
                .licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
                .version("2.0")
                .build();
    }


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

Please let me know if i am doing anything wrong here....I used to start my microservice as a boot application in my Eclipse and test all my micro services... And now i am accessing http://localhost:8081/swagger-ui.html to see the swagger api documentation.

I am sure there is a mistake the way i am integrating but not able to catch it...I hope you would have done lots of such integration and little clue will help me proceeding with this...

Thanks in advance !!

@dilipkrish
Copy link
Member

Remove the lines, in your configuration

@Configuration
@EnableWebMvc

I think that should fix it for you.

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

@dilipkrish - Now i dont see the error which i was seeing before on the server log "No mapping found for HTTP request with URI [/swagger-ui.html]" .

But i am not seeing the API documentation page in my browser . Its the same error page:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Wed Jun 10 10:41:06 PDT 2015
There was an unexpected error (type=Not Found, status=404).
No message available

@dilipkrish
Copy link
Member

Your WebMvcConfiguration is interfering with the springboot auto configuration. You need to either do everything manually yourself or remove that class and just configure the message converters as a bean.

I would suggest read up on how springboot works as compared to spring-mvc.

@dpatra1
Copy link
Author

dpatra1 commented Jun 10, 2015

:)

Thanks for the update !

@dpatra1
Copy link
Author

dpatra1 commented Jun 14, 2015

@dilipkrish - I am able to fix the issue and bring the swagger-ui.html working in my local.

But i have couple of things which i wanted to configure:

  1. I am seeing bunch of other information int eh swagger-ui other than my API information. How can i stop displaying those default information ?

  2. My API informaiton is coming under "V1" which i am confused like from where its picking up. I wanted to display APIS based on the resource URL only . Can you help me where can i configure those ?

Thnaks,
Deba

@dilipkrish
Copy link
Member

@dpatra1 you can use the selector api and implement your own predicates if required. The getting started section has more info.

springfox_reference_documentation

Closing this issue as the origin issue has been addressed/answered

@dpatra1
Copy link
Author

dpatra1 commented Jul 15, 2015

@dilipkrish This is a old post and i have figured that out long back.

@Nantha2016
Copy link

Hi,

I am using springfox to integrate swagger UI with spring boot application. I have exposed few rest APIs and have secured them using spring security.
When I try to access these secured APIs, I need to pass JSESSIONID as a cookie along with the request. I tried passing this value as below-
List list = new ArrayList();
list.add(new ParameterBuilder()
.name("JSESSIONID")
.description("JSESSIONID")
.modelRef(new ModelRef("string"))
.parameterType("cookie")
.required(true)
.build());
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build().enable(true)
.enableUrlTemplating(true)
.globalOperationParameters(list);
But when I submit the request from swagger-ui, it doesnt pass this value as a cookie.
Is there any other way of achieving this?
Please help.

@dilipkrish
Copy link
Member

@Nantha2016 Cookies are not supported in swagger-ui I believe.

@Nantha2016
Copy link

Hi Dilip,

Thanks for the link.
My Rest API will not be hit unless its authenticated against cookie jsessionid value. How is this case handled in swagger UI?
I am able to achieve this using curl command from the command prompt.
What I want is something like-
curl --cookie "jsessionid value"
Please let me know if there is a way.

@dilipkrish
Copy link
Member

You'd have to either implement it in your private copy of swagger-ui or push them to implement it. 😢

@objectivePinta
Copy link

objectivePinta commented Sep 5, 2016

I'm having the same old issue:) and I think I have tried everything.
I'm using spring boot -> packaged as a jar.
http://localhost:8080/api/v2/api-docs?group=myApi -> return a big json with all my apis.
But http://localhost:8080/api/swagger-ui.html -> returns
There was an unexpected error (type=Method Not Allowed, status=405).
Those are the dependencies in the pom

  <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.5.0</version>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.5.0</version>
      <scope>compile</scope>
    </dependency>

My SwaggerConfig class:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Autowired
    private Environment env;

    @Bean
    public Docket api() {

        Docket docket = new Docket(DocumentationType.SWAGGER_2).groupName("myApi").
                select()
                .apis(RequestHandlerSelectors.basePackage("net.ws.controller"))
                .paths(PathSelectors.any()).build();
        return docket;
    }


}

Any idea what I'm doing wrong?
Thanks!

@dilipkrish
Copy link
Member

What version of swagger UI are you using?

This is a very old issue, please open a new issue if you feel its a new issue, it will help us prioritize it better.

@objectivePinta
Copy link

Sorry for writing here. I don't know if it's old or new. I tried with older versions as well, and same result. But I'm using 2.5.0 now.

@dilipkrish
Copy link
Member

Since its a javascript error it would be useful to know what the URL was that failed with a 405 error.

@objectivePinta
Copy link

I tried with server.contextPath=/ and server.contextPath=/random
something like localhost:8080/swagger-ui.html -> run directly from the IDE.
I was thinking it might have something with the fact it's built as a jar?
Also we use spring security, but I added /swagger-ui.html, together with the swagger paths to be allowed without asking for login.

@dilipkrish
Copy link
Member

spring security that would be it! you'd need to add to the list of path exceptions /swagger-resources/* as well

@helloworldtang
Copy link

helloworldtang commented Jan 18, 2017

@dilipkrish @objectivePinta
I met the same question.
reason: there is some wrong in the use of @RequestMapping

fix the bugs in the use of mvc ,then swagger-ui appeared in all its glory.

the error stack when open http://localhost/swagger-ui.html in the browser 👍
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:207) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:374) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:314) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:61) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:352) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1131) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:936) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.6.jar:8.5.6] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) [spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) [tomcat-embed-core-8.5.6.jar:8.5.6] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317) [spring-security-web-4.1.4.RELEASE.jar:4.1.4.RELEASE]

@objectivePinta
Copy link

Sorry for answering so late. My problem was that the login endpoint had as request mapping "/". Make sure none of your rest end points answer to the default path. After changing it to "/login", swagger-ui appeared in all its glory.

@MadhuraMaddali
Copy link

MadhuraMaddali commented Apr 4, 2017

Hi,
I am trying to apply a custom Content Negotiagtion scheme only for my REST Endpoints and Swagger needed a deault one.
To acheive this I ended up adding two diffrerent Dispatcher servlets. One for swagger and one for REST calls. It was working fine and I was using Spring 4 with XML configuration.

Recently we started moving it to Spring Boot and started migrating to JavaConfig, and I am stuck with an issue now. If I enable @configuration tag in WebConfig file, it will Not display The swagger page End points.
The reason is ContentNegotiation Manager is getting applied to Swagger UI Page and I am not understanding how to apply it to only REST endpoints.
I tried many ways with no luck. Any help is greately appreciated.

{code}
package com.inq.test;

import com.inq.test.config.WebConfig;
import com.inq.test.doc.SwaggerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

TestAPIApplication.class
{code}
@SpringBootApplication(exclude={DispatcherServletAutoConfiguration.class})
public class TestAPIApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(testAPIApplication.class, args);
}

@bean
public ServletRegistrationBean test() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(WebConfig.class);
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/testAPI/v2/test/*");
servletRegistrationBean.setName("test");
servletRegistrationBean.setAsyncSupported(true);
return servletRegistrationBean;
}

@bean
public ServletRegistrationBean testSwagger() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(SwaggerConfig.class);
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/testAPI/v2/test/doc/*");
servletRegistrationBean.setName("test-swagger");
servletRegistrationBean.setAsyncSupported(true);
return servletRegistrationBean;
}

}
{code}

SwaggerConfig class
{code}
package com.inq.test.doc;

import com.google.common.base.Predicates;
import com.inq.test.config.AppConfig;
import com.inq.test.config.WebConfig;
import com.inq.test.config.oauth2.OAuth2ResourceServerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;
import org.springframework.web.context.ServletContextAware;
import springfox.documentation.PathProvider;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ImplicitGrantBuilder;
import springfox.documentation.builders.OAuthBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.GrantType;
import springfox.documentation.service.LoginEndpoint;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.paths.RelativePathProvider;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.ApiKeyVehicle;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.servlet.ServletContext;
import java.util.List;

import static com.google.common.collect.Lists.newArrayList;

@import({ AppConfig.class })
@configuration
@componentscan(excludeFilters={
@componentscan.Filter(type= FilterType.ASSIGNABLE_TYPE, value=WebConfig.class),
@componentscan.Filter(type= FilterType.ASSIGNABLE_TYPE, value=OAuth2ResourceServerConfig.class)})
@EnableSwagger2
public class SwaggerConfig implements ServletContextAware {
private static final String SECURITY_SCHEMA_OAUTH2 = "oauth2";
private ServletContext servletContext;

@Value("${springfox.documentation.swagger.v2.host}")
private String hostName;

@Value("${swagger.ui.oauth2.authorize.url}")
private String authorizeUrl;

@Value("${swagger.ui.oauth2.clientId}")
private String swaggerAppClientId;

@Value("${swagger.ui.oauth2.clientSecret}")
private String swaggerClientSecret;

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .pathProvider(pathProvider())
            .host(hostName)
            .select()
            .apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot")))
            .build()
            .apiInfo(apiInfo())
            .securitySchemes(newArrayList(oauth()));
}

@Bean
public SecurityScheme oauth() {
    return new OAuthBuilder()
            .name(SECURITY_SCHEMA_OAUTH2)
            .grantTypes(grantTypes())
            .scopes(scopes())
            .build();
}

@Bean
public SecurityConfiguration securityInfo() {
    return new SecurityConfiguration(swaggerAppClientId, swaggerClientSecret, "realm", "swagger", "", ApiKeyVehicle.HEADER, "", " ");
}

private List<AuthorizationScope> scopes() {
    return newArrayList(
            new AuthorizationScope("write", "write and read"),
            new AuthorizationScope("read", "read only"));
}

private List<GrantType> grantTypes() {
    GrantType grantType = new ImplicitGrantBuilder()
            .loginEndpoint(new LoginEndpoint(authorizeUrl))
            .tokenName("access_token")
            .build();

    return newArrayList(grantType);
}

private PathProvider pathProvider() {
    return new RelativePathProvider(servletContext) {
        @Override
        protected String applicationPath() {
            return "/testAPI/v2/test/doc";
        }
    };
}

private ApiInfo apiInfo() {
    return new ApiInfoBuilder()
            .title("Test API")
            .description("The TouchCommerce Test engagement API is designed to allow any application that can support the HTTP protocol "
                    + "to develop or extend existing applications to implement TouchCommerce Test services using a REST API")
            .version("2.0")
            .build();
}

@Override
public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
}

}
{code}

WebConfig class

{code}
package com.inq.test.config;

import com.inq.test.config.AppConfig;
import com.inq.test.doc.SwaggerConfig;
import com.inq.test.services.testDeferredResultInterceptor;
import com.inq.test.web.testParameterContentNegotiationStrategy;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@configuration
//@EnableWebMvc
//@EnableAsync
@import({ AppConfig.class })
@componentscan(basePackages = "com.inq.test",
excludeFilters={@componentscan.Filter(type= FilterType.ASSIGNABLE_TYPE, value=SwaggerConfig.class)})
public class WebConfig extends WebMvcConfigurerAdapter {

@OverRide
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).
useJaf(false).
ignoreAcceptHeader(true).
// parameterName("output").
defaultContentTypeStrategy(new testParameterContentNegotiationStrategy());
}

@Bean
public FilterRegistrationBean corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    config.addExposedHeader("Authorization");
    config.addExposedHeader("Content-Type");
    source.registerCorsConfiguration("/**", config);
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;
}

@Override
  public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    System.out.println("Test ");
    DeferredResultProcessingInterceptor testDeferredResultInterceptor = new testDeferredResultInterceptor();

    configurer.registerDeferredResultInterceptors(testDeferredResultInterceptor);
    configurer.setDefaultTimeout(50000); //in milliseconds

  }

/*@OverRide
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(50000); //in milliseconds
super.configureAsyncSupport(configurer);
} */

/* @OverRide
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new testDeferredResultInterceptor());
} */

/* @OverRide
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
*/
}
{code}

@configuration
//@componentscan(basePackages = {"com.inq.test"})
@componentscan(basePackages = "com.inq.test",
excludeFilters = {@componentscan.Filter(type = FilterType.ANNOTATION, value=org.springframework.context.annotation.Configuration.class),
@componentscan.Filter(type = FilterType.ASSIGNABLE_TYPE, value=WebConfig.class),
@componentscan.Filter(type = FilterType.ASSIGNABLE_TYPE, value=SwaggerConfig.class)})
@propertysource({"classpath:application.properties"})
@EnableScheduling
public class AppConfig {
.....
}

{code}

@Piyushjoshi07
Copy link

Hello @dilipkrish I read the above comments made by you and it solved my problem. Thanks a lot.

@ianynchen
Copy link

I had this problem, and I have this setting: spring.resources.add-mappings=false in my application.properties file. Commenting out this line will make springfox swagger work, but my ResponseEntityExceptionHandler won't work for me. I need it to generate a custom error message back for any exception the user might encounter

@sourcerebels
Copy link

I was able to run swagger-ui and having custom error messages in my endpoints by modifying WebMvcConfigurerAdapter. But I'm not really sure if this has side effects. This is my code:


@Configuration
@EnableWebMvc
public class XcareVitalinqConnectorWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations(
                        "classpath:/META-INF/resources/webjars/");

        registry.addResourceHandler("/swagger-ui.html")
                .addResourceLocations(getStaticLocations());
    }

    private String[] getStaticLocations() {

        String[] result = new String[5];
        result[0] = "/";
        result[1] = "classpath:/META-INF/resources/";
        result[2] = "classpath:/resources/";
        result[3] = "classpath:/static/";
        result[4] = "classpath:/public/";

        return result;
    }
}

@dilipkrish
Copy link
Member

Using @EnableWebMvc does have consequences. It will not auto configure your mvc components

@geektcp
Copy link

geektcp commented Dec 1, 2018

I found the really reason, because of @componentscan, and u must to annotate it , for example:
//@componentscan(basePackages = {"com.haizhi.dao","com.haizhi.controll","com.haizhi.service"})

and then , it will be ok, u can open the swagger-tui.html and /swagger-resources/configuration/ui

@geektcp
Copy link

geektcp commented Dec 1, 2018

swagger启动后,老是报错下面的url无法打开swagger-ui.html,
提示内部url无法打开swagger-resources/configuration/ui

原因是:
启用了组件扫描导致:@componentscan(basePackages = {"com.haizhi.dao","com.haizhi.controll","com.haizhi.service"})
这个问题真是费解。

解决办法:注释掉扫描注解即可:
//@componentscan(basePackages = {"com.haizhi.dao","com.haizhi.controll","com.haizhi.service"})

@vinayyr
Copy link

vinayyr commented Apr 11, 2019

Your WebMvcConfiguration is interfering with the springboot auto configuration. You need to either do everything manually yourself or remove that class and just configure the message converters as a bean.

I would suggest read up on how springboot works as compared to spring-mvc.

Hii, my application is inheriting from WebMvcConfiguration ,and i am getting 404 eror for swgger,how can i fix that

@wx8900
Copy link

wx8900 commented Jul 16, 2019

I integrated swagger 2 and Spring Boot, and I found a error as following. How to fix this issue?

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon Jul 15 22:02:19 PDT 2019
There was an unexpected error (type=Not Found, status=404).
No message available

@Kakau-preto
Copy link

@wx8900 hi,
in this moment i have the same error: Whitelabel Error Page
How i can resolve.

please help me.

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