Swagger UI does not show all the controllers of my project - spring-boot

I have few controllers which have the same request path prefix i.e.
/v4/users/{userId}/
And then the methods in the controllers are annotated with separate path mappings. The swagger ui shows only one of those controllers.
#Component
public class BaseController {
}
/******************/
#RestController
#RequestMapping("/v4/users/{userId}/payment")
public class PaymentController extends BaseController {
#RequestMapping(value = "/initiate", method = RequestMethod.POST)
public HttpEntity<Object> initiatePayment() {...}
#RequestMapping(value = "/success", method = RequestMethod.GET)
public HttpEntity<Object> success() {...}
}
#RestController
#RequestMapping("/v4/users/{userId}/preferences")
public class CustomerPreferencesController extends BaseController{
#RequestMapping(value = "/deals/{dealId}", method = RequestMethod.POST)
public HttpEntity<Object> favouriteDeal(...) {...}
//some more methods
}
There is one more controller that has the same path prefix i.e. /v4/users/{userId} which shows up on swagger-ui but not the above two controllers.
pom.xml entries:
<!-- Swagger Spring -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.0.2</version>
</dependency>
UPDATE - 1
#Bean
public Docket mobileAPI() {
return new Docket(DocumentationType.SWAGGER_2)
.host(swaggerHost)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
.pathMapping("/");
}

Related

Getting swagger-ui to display my codegen-generated API

I'm working on a springboot project. We're doing API first so we're generating code from an api.yaml. We're using openapi 3.0. The interfaces are being generated fine but when we browse to our swagger-ui URL, it says No operations defined in spec!
Here are the details:
#Configuration
#RequiredArgsConstructor
#EnableSwagger2
public class SwaggerConfig {
private final BuildProperties buildProperties;
#Bean
public Docket docketConfig() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors
.basePackage("com.xyz.infrastructure.rest.spring.resource"))
.build().apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(buildProperties.getName())
.version(buildProperties.getVersion())
.build();
}
}
Our structure is:
com.xyz.infrastructure.rest.spring
|
|- config
|- SwaggerConfig
|- spec //autogenerated
|- dto //autogenerated
|- resource // implementations of interfaces found in spec
What are we missing?
We're using:
<dependency>
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen</artifactId>
<version>3.0.21</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
Thank you!
We finally got it to work. Here's what did it for us:
Springfox version 3:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Using the io.swagger.codegen.v3 plugin.
This swagger config:
#Configuration
#EnableOpenApi
public class SwaggerConfig {
#Bean
public Docket docketConfig() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors
.basePackage("com.xyz.infrastructure.rest.spring.resource"))
.build();
}
}
And make sure your classes in resource have the #RestController annotation.
I used like below:
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
//.apis(RequestHandlerSelectors.any())
.apis(RequestHandlerSelectors.basePackage("uz.xb.qr_project"))//*** base package
.paths(PathSelectors.any())
.build()
.apiInfo(metaData());
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title("Spring Boot REST API")
.description("\"Spring Boot REST API for Online Store\"")
.version("1.0.0")
.license("Apache License Version 2.0")
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0\"")
.contact(new Contact("John Thompson", "https://springframework.guru/about/", "john#springfrmework.guru"))
.build();
}
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")//**address of swagger ui
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
ApiController.java
#RestController
#RequestMapping
public class ApiController {
#Autowired
private ApiService apiService;
#GetMapping
public String swagger(){
return "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>REST API LIST</title>\n" +
" <meta http-equiv=\"refresh\" content=\"0; url=/qr_online/swagger-ui.html\" />\n" +
"</head>\n" +
"<body>\n" +
"REST API LIST\n" +
"</body>\n" +
"</html>";
}
//Or you can use like this inside controller
/*
#GetMapping
public void redirect(HttpServletResponse response) throws IOException {
response.sendRedirect("swagger-ui.html");
}
*/
}
pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

springfox-data-rest configuration not work

I follow the link to config.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-data-rest</artifactId>
<version>2.7.0</version>
</dependency>
#Configuration
#Import( {springfox.documentation.spring.data.rest.configuration.SpringDataRestConfiguration.class})
public class WebMvcConfig extends WebMvcConfigurerAdapter{
......
}
#RepositoryRestResource(collectionResourceRel = "mails", path = "mails")
public interface TMessageMailDao extends PagingAndSortingRepository<TMessageMail, Long>{
}
But when I open http://localhost:8080/swagger-ui.html, there is nothing.
I know springfox-data-rest is still in incubation. Is that the reason it's not work? Or anything wrong?
You are missing #EnableSwagger2 annotation.
Make sure to create a Docket bean as shown in the example below.
#Configuration
#Import({SpringDataRestConfiguration.class})
#EnableSwagger2
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("example")
.select()
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo("Example API", "Example API"));
}
private ApiInfo apiInfo(String title, String description) {
return new ApiInfoBuilder()
.title(title)
.description(description)
.build();
}
}
Make sure to add the following dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
Once it's working, your swagger-ui.html will look something like this:
I want to scan two packages. How to include two base packages and not one?
Just Controllers
If you are just interested in including REST controllers and not any repository, you can specify any number of packages within apis method of Docket with the help of a custom method.
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("book")
.select()
.apis(exactPackages("com.basaki.controller", "com.basaki.model"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo("Example Springfox API",
"Example Springfox API"));
}
private static Predicate<RequestHandler> exactPackages(
final String... pkgs) {
return input -> {
String currentPkg =
input.declaringClass().getPackage().getName();
for (String pkg : pkgs) {
if (pkg.equals(currentPkg)) {
return true;
}
}
return false;
};
}
Controllers and Repositories
If you are interested in including REST controllers and repositories, you have to take advantage of paths method in Docket with the help of a custom method. The custom method takes path regexes.
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("book")
.select()
.paths(matchPathRegex("/books(/|$).*",
"/booxs(/|$).*", "/tokens(/|$).*",
"/ping(/|$).*"))
.build()
.apiInfo(apiInfo("Example Springfox API",
"Example Springfox API"));
}
private static Predicate<String> matchPathRegex(final String... pathRegexs) {
return new Predicate<String>() {
#Override
public boolean apply(String input) {
for (String pathRegex : pathRegexs) {
if (input.matches(pathRegex)) {
return true;
}
}
return false;
}
};
}
Thank you for Indra Basak 's help.
My configuration just have one problem.
#Configuration
#EnableSwagger2
#Import({SpringDataRestConfiguration.class})
This three annotations have to use together. I config #EnableSwagger2 in another configuration file.

Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type:

I am trying to expose REST service, but while hitting it from POSTMAN i am getting below :
WARNING: Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.ArrayList
Where as i have also included below jar files which are required :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.1</version>
</dependency>
Here is my REST controller Code :
#RestController
#RequestMapping("/MayankAPI")
public class TestRestAPI {
#RequestMapping(value="/sayHello" , method = RequestMethod.POST)
public TestPojo postData(#RequestBody String payload) {
System.out.println("Hello post"+payload);
TestPojo payload1=new TestPojo();
payload1.setStudentName("Jack");
return payload1;
}
#RequestMapping(value="/sayHello" , method = RequestMethod.GET)
public List<TestPojo> getData(String payload) {
System.out.println("Hello get"+payload);
List<TestPojo> payload1=new ArrayList<TestPojo>();
TestPojo tp = new TestPojo();
tp.setStudentName("Jack");
payload1.add(tp);
return payload1;
}
Here is my bean which i am trying to return :
public class TestPojo {
private String studentName;
private String studentId;
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
}
Please help me where i am doing wrong.
I know it too late but still
Alternate Solution:
you need to enable Spring project as Web MVC as follow:
#Configuration
#EnableWebMvc
#ComponentScan("basePackages = com.test")
public class MiBenefitConfiguration
{
}
This is happening because MappingJackson2HttpMessageConverter is not registered in my App config file as below.
#Configuration
#ComponentScan("basePackages = com.test")
public class MiBenefitConfiguration extends WebMvcConfigurationSupport{
#Bean
public ObjectMapper getObjectMapper() {
return new ObjectMapper();
}
#Bean
public MappingJackson2HttpMessageConverter messageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(getObjectMapper());
return converter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(messageConverter());
addDefaultHttpMessageConverters(converters);
}
}

Swagger configured in Spring Boot shows only methods with POST and GET mapping

Swagger configured in Spring Boot shows only one method with POST mapping and one method with GET mapping from every controller. Swagger ignores another methods with GET and POST mapping and ignores all methods with PUT and DELETE mappings. My configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.project.controllers"))
.paths(PathSelectors.ant("/api/*"))
.build();
}
}
Dependency in pom.xml:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
My controllers code:
#RestController
#RequestMapping(value = "/api/users", produces = "application/json; charset=UTF-8")
public class UserController {
#Autowired
private UserService userService;
protected UserService getService() {
return userService;
}
#RequestMapping(method = GET)
public Page<User> query(#RequestParam Map<String, Object> parameters, Pageable pageable) {
return getService().query(parameters, pageable);
}
#ResponseStatus(CREATED)
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<User> create(#RequestBody User entity) {
return ResponseEntity.status(HttpStatus.CREATED).body(getService().create(entity));
}
#RequestMapping(value = "/{id:[0-9]+}", method = RequestMethod.PUT)
public ResponseEntity<User> update(#PathVariable Long id, #RequestBody User entity) {
return ResponseEntity.ok(getService().update(id, entity));
}
#RequestMapping("/current")
public ResponseEntity current() {
return ResponseEntity.ok(userService.getUser());
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/enable", method = RequestMethod.POST)
public void enable(#PathVariable("id") final long id) {
userService.enable(id);
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/disable", method = RequestMethod.POST)
public void disable(#PathVariable("id") final long id) {
userService.disable(id);
}
#RequestMapping(value = "/histories", method = RequestMethod.GET)
public List<UserHistory> histories() {
return userService.histories();
}
}
May be i need add some more configuration or add something else?
Based on your controller, I think you should add one more star in the path matcher in your swagger config:
.paths(PathSelectors.ant("/api/**"))
e.g /api/users/current would not be matched by the /api/* but by /api/**, and this why you are getting only the base path endpoints documented.

Thymeleaf won't fetch pages

this is my Spring Boot with Thymeleaf setup.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
ThymeleafConfig
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode("LEGACYHTML5");
resolver.setOrder(1);
return resolver;
}
}
Controller
#RestController
public class WebController {
#RequestMapping("")
public String index(){
return "index";
}
}
Index.html is located in src/main/resources/templates.
But when localhost:8080 is called only "index" string is rendered. Index.html is not fetched. What could be the problem?
You're using a #RestController.
All handler methods of a #RestController bean act as if annotated with #ResponseBody, ie. the object they return is written to the response directly based on some HttpMessageConverter.
Change #RestController to #Controller if you don't want that behavior.

Resources