Swagger 2.0 support in Grails 3+ (3.0.11) not working - spring

I am running Grails 3.0.11 and want to create Swagger-documentation for my REST endpoints. I added the SwaggyDoc-plugin to the dependencies in my build.gradle script by adding:
compile "org.grails.plugins:swaggydoc:0.26.0".
In IntelliJ I see the Swaggydoc-dependency added to my list of libraries.
After starting my Grails-application via the grails run-app command and opening my application by entering http://localhost:8080/api/ I get an 404 error telling the page does not exist.
Do I need to configure something more or to run something special to generate documentation? I already tried to open a ticket in the Git-project and contacting the author with no succes.
Update1: there seems to be a Grails 3-plugin (found at Versioneye?) which I added:
compile "org.grails.plugins:swaggydoc-grails3:0.26.0"
It does work half, by default some sort of Pet-demo is visible and it is failing on constraints in a domain and enums. Doesn't seem to work very well actually.
Update2: As pointed out by Dilip Krishnan I tried to use SpringFox, first I added the dependencies to my Gradle build file:
compile("io.springfox:springfox-swagger2:2.3.1")
compile("io.springfox:springfox-swagger-ui:2.3.1")
Then I added a new class called ApiDocumentationConfiguration with the following code:
#Configuration
#EnableSwagger2
public class ApiDocumentationConfiguration {
#Bean
public Docket documentation() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
#Bean
public UiConfiguration uiConfig() {
return UiConfiguration.DEFAULT;
}
private ApiInfo metadata() {
return new ApiInfoBuilder()
.title("My awesome API")
.description("Some description")
.version("1.0")
.contact("my-email#domain.org")
.build();
}
}
My Grails resources file contains the following code:
beans = {
apiDocumentationConfiguration(ApiDocumentationConfiguration)
}
Last step was starting the application and trying to load the end point which shows the Swagger front end:
http://localhost:8080/swagger-ui.html
It behind the scenes tries to load an other end point (containing the JSON I guess?) which loads
http://localhost:8080/v2/api-docs
This does show JSON data and I get end points for things like a basic error controller, health mvc, metrics mvc et cetera. But not my own annotated user controller which is annotated like follows:
#Api(value = "users", description = "Endpoint for user management")
class UserController {
// GET all users
#ApiOperation(value = "doStuff", nickname = "doStuff", response = User.class)
def index() {
respond User.list()
}
}
Seems I am almost there, but still missing something, is my annotation wrong or doesn't it scan my controllers?
Update3: in contact with one of the authors of SpringFox (Dilip Krishnan) to add support for Grails 3+ to SpringFox, see ticket. The reason it doesn't currently work is because SpringFox looks at MVC annotation, an adapter needs to be written to retrieve the endpoints from the controllers in Grails.

I have successfully used swaggydocs in both 2.4.x projects and 3.1.4.
In order to make it work in 3.x (tested on 3.1.4) you have to add
compile "org.grails.plugins:swaggydoc-grails3:0.26.0"
to gradle dependencies section. That makes swaggy available in your project.
Then add annotations to your controllers
#Api("test methods")
class TestController {
#ApiOperation(value = "some method description")
#ApiResponses([
#ApiResponse(code = 405, message = "Bad method. Only POST is allowed"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 400, message = "Invalid request json")
])
def testGetMethod() {
render([status: "OK"] as JSON)
}
Then mark your methods allowedMethods as follows
class TestController {
static allowedMethods = [testGetMethod: "GET", testPostMethod: "POST"]
NOTE this is really important - otherwise swaggy will mark every your method as GET. Swaggy doesn't respect neither httpMethod in ApiOperation annotation nor http method in url mappings.
Finally add your controller to urlmappings as swaggy checks url mappings to look for URLS. Note camelCase!
//NOTE small camelCase
//swaggy won't see urls correctly if you start controller name with capital letter
"/api/test/"(controller: "test", action: "testGetMethod")
"/api/test/"(controller: "test", action: "testPostMethod")
You can also add some api info in application.yml
swaggydoc:
contact: rafal#pydyniak.pl
description: sample swaggy app
You can find sample app (with dummy methods but point was to make swaggy work) at my github https://github.com/RafalPydyniak/Swaggy-example.
Also there are some more docs on how to use this api on http://rahulsom.github.io/swaggydoc/. I just wanted to show you how to install it (as it's quite tricky to make everything work)
Hope it helps!

I followed the same 2 steps:
1) add swagger2 dependencies
2) provide configuation
The problem is that sprinfox does not scan grails url mappings (see https://github.com/springfox/springfox/issues/1169#issuecomment-252259284)
To fix that I added standard spring annotations:
#Controller()
#RequestMapping(value="/path")
on the controller and
#RequestMapping(value="/resource")
on the method. After that sprinfox started to pick up my documentation.

Related

How do I configure a custom URL for the springdoc swagger-ui HTML page?

After adding the springdoc-openapi-ui dependency to my Spring project (not Spring Boot) OpenAPI V3 documentation is generated and can be viewed using the default swagger-ui page: localhost:8080/swagger-ui.html. Because the springdoc documentation replaces previous Swagger documentation I want to make it available at the same URL, localhost:8080/docs/index.html. Based on the springdoc documentation I get the impression that can be done by using the springdoc.swagger-ui.path option in the application.properties:
springdoc.swagger-ui.path=/docs/index.html
However, where I would expect to be able to navigate to the API documentation by going to localhost:8080/docs/index.html I get a 404 instead, localhost:8080/swagger-ui.html still works but now redirects to http://localhost:8080/docs/swagger-ui/index.html?configUrl=/restapi/v3/api-docs/swagger-config.
How can I configure my project too make the swagger-ui page available through a custom URL, i.e. localhost:8080/docs/index.html instead of the default localhost:8080/swagger-ui.html?
Edit
After trying some more to get it working and looking through the available information online, such as the springdoc FAQ (mentioned in this answer by H3AR7B3A7) I couldn't get it working. I've decided to go with a different solution which should have the same effect. The springdoc.swagger-ui.path option allows specifying a custom URL but, as I understand it, going to the custom URL redirects a user to the standard localhost:8080/swagger-ui.html page. So the redirect is now configured manually:
#RequestMapping("/docs/index.html")
public void apiDocumentation(HttpServletResponse response) throws IOException {
response.sendRedirect("/swagger-ui.html");
}
I had a similar task for Spring Boot 2.5.6 and springdoc-openapi-webflux-ui 1.5.12. I've found several possible solutions for myself. Maybe it will be helpful for somebody else.
Set springdoc.swagger-ui.path directly
The straightforward way is to set property springdoc.swagger-ui.path=/custom/path. It will work perfectly if you can hardcode swagger path in your application.
Override springdoc.swagger-ui.path property
You can change default swagger-ui path programmatically using ApplicationListener<ApplicationPreparedEvent>. The idea is simple - override springdoc.swagger-ui.path=/custom/path before your Spring Boot application starts.
#Component
public class SwaggerConfiguration implements ApplicationListener<ApplicationPreparedEvent> {
#Override
public void onApplicationEvent(final ApplicationPreparedEvent event) {
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
Properties props = new Properties();
props.put("springdoc.swagger-ui.path", swaggerPath());
environment.getPropertySources()
.addFirst(new PropertiesPropertySource("programmatically", props));
}
private String swaggerPath() {
return "/swagger/path"; //todo: implement your logic here.
}
}
In this case, you must register the listener before your application start:
#SpringBootApplication
#OpenAPIDefinition(info = #Info(title = "APIs", version = "0.0.1", description = "APIs v0.0.1"))
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
application.addListeners(new SwaggerConfiguration());
application.run(args);
}
}
Redirect using controller
You can also register your own controller and make a simple redirect (the same as what you suggest, but in my case, I need to use the WebFlux approach):
#RestController
public class SwaggerEndpoint {
#GetMapping("/custom/path")
public Mono<Void> api(ServerHttpResponse response) {
response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
response.getHeaders().setLocation(URI.create("/swagger-ui.html"));
return response.setComplete();
}
}
The problem with such an approach - your server will still respond if you call it by address "/swagger-ui.html".
Apparantly the library integrates natively only with spring-boot applications like you mentioned in your comment.
If you want to use spring, it's possible but the integration details aren't documented, because it really depends on the version/module and the nature of you spring application.
You can check the FAQ to see if it answers your questions.
There are some more answers here on SO.
I solved by a clear code.
The problem is in webflux that security need permitAll for some uri resources so I solved in this mode in WebFlux Security I image the same in Springboot no WebFlux:
.authorizeExchange().pathMatchers(
// START To show swagger 3:
"/swagger-ui.html",
"/webjars/swagger-ui/**",
"/v3/api-docs/swagger-config",
"/v3/api-docs", // This is the one URI resource used by openApi3 too.
// END To show swagger 3:
"/other-uri-permitAll",
...
"/other-uri-permitAll_N",
.permitAll()
.and()
.authorizeExchange().pathMatchers(
"/other-uri-authenticated-only_1",
"...",
"/other-uri-authenticated-only_1")
.authenticated()
.and()
.authenticationManager(myAuthenticationManager)
.securityContextRepository(mySecurityContextRepository)
.authorizeExchange().anyExchange().authenticated()
.and()
.build();

How to migrate this mutlipart file upload to Spring Boot 2.4?

With Spring Boot 2.3 I was using the following Kotlin code
val mvcResultImage = this.mockMvc!!.perform(MockMvcRequestBuilders.multipart("/somepath)
.file("files[]", imageFile.getBytes())
.characterEncoding("UTF-8"))
.andReturn()
in an integration test for a controller with a function
#PostMapping(path = ["/somepath"],
consumes = [MediaType.MULTIPART_FORM_DATA_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE])
#ResponseBody
fun createFromBytes(#RequestParam("files[]") file: MultipartFile): ResponseEntity<Any> {
...
}
In 2.3 I was able to handle the request in the controller function whereas in 2.4 the controller function raises a org.springframework.web.multipart.support.MissingServletRequestPartException with the message Required request part 'files[]' is not present and causes HTTP response code 400.
I don't find anything in the migration guide and list of handled issues for this version change.
A rename to file in both controller and request doesn't help, I don't remember why I added [] in the code working with 2.3, but I think it was necessary to make it work.
I'm using Spring Boot through the maven parent mechanism with spring-boot-starter-parent:2.4.1.
This is a known issues in Spring Boot coming from Spring. It's fixed in Spring Boot 2.4.2. The linked issue contains a successfully tested workaround in case you're stuck with 2.4.1: Create MockMultipartFile with MockMultipartFile( String name, #Nullable String originalFilename, #Nullable String contentType, #Nullable byte[] content) (specification of originalFilename matters).

Springboot, CXF 3.2.7, Swagger2Feature: Authorization header does not appear in the request headers

I'm trying to integrate Swagger 2 into my API, which is implemented with CXF newest version: 3.2.7.
I tried lots of tutorials, the CXF official documentation, others too (e.g. Spring Boot, Apache CXF, Swagger under JAX-RS).
The swagger official website does not help for me. The swagger OpenAPI 2.0 authentication doc is not working, neighter the OpenAPI 3.0.
It is not working with component schemes of Open API 3.0.0, so i stayed with the apiKeyDefinition.
The one, which is working now can be found in this thread, in the answer of #Naoj:
CXF Swagger2Feature adding securityDefinitions
With this solution the swagger ui appeared and also the Autorize button is showing.
I fill the authentication form, and after that, I try to send requests with the swagger-ui. The problem is, that the Authorization header does not appear in the request, so I got 401 response.
In the pom:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description-swagger</artifactId>
<version>3.2.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>3.20.1</version>
</dependency>
My swagger configuration looks like this:
#Configuration
public class SwaggerConfig {
#Bean
#ConfigurationProperties("swagger")
public Swagger2Feature swagger() {
return new ExtendedSwagger2Feature();
}
#Bean
#DependsOn("jaxRsServer")
public ServletContextInitializer initializer() {
return servletContext -> {
BeanConfig scanner = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = scanner.getSwagger();
servletContext.setAttribute("swagger", swagger);
};
}
}
The extended swagger feature is the following:
#Provider(value = Provider.Type.Feature, scope = Provider.Scope.Server)
public class ExtendedSwagger2Feature extends Swagger2Feature {
#Override
protected void addSwaggerResource(Server server, Bus bus) {
super.addSwaggerResource(server, bus);
BeanConfig config = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = config.getSwagger();
swagger.securityDefinition("Bearer", new ApiKeyAuthDefinition("authorization", In.HEADER));
}
}
I try to configure Bearer JWT token based authentication. My application.yml contains the following:
swagger:
basePath: /rest
title: Backend Application
description: Swagger documentation of Backend Application REST services
license:
licenceUrl:
contact:
resourcePackage: my.resource.package
scan: true
apiKeyAuthDefinition:
name: Authorization
in: header
type: http
I import the SwaggerConfig into my #SpringBootApplication class like this:
#Import(SwaggerConfig.class)
It is working, as i see, swagger appeared and the title and description field is filled with the properties of my yml.
What am I missing? Any suggestions would be appretiated.
Thanks in advance.
You can simplify your code and remove ExtendedSwagger2Feature and the initializer(). Modify your swagger() method as follows and the output will be the same:
#Bean
#ConfigurationProperties("swagger")
public Swagger2Feature swagger() {
Swagger2Feature swagger2Feature = new Swagger2Feature();
swagger2Feature.setSecurityDefinitions(Collections.singletonMap("bearerAuth",
new ApiKeyAuthDefinition("Authorization", In.HEADER)));
return swagger2Feature;
}
The reason for the token not being added to your request, is that securityDefinitions is just a declaration for the available schemes. You need to apply it to the operation by adding (to your TestResource interface):
#Api( authorizations = #Authorization( value = "bearerAuth" ))
You will notice a lock icon next to the operation in Swagger UI. Currently it's not there.
Btw. you should use the newer OpenApiFeature instead of the old Swagger2Feature. Happy to answer questions, if you have problems with it.

Swagger ui adds an additional /path in the testing page when behind zuul

I have several microservices behind a spring boot application embedding zuul, let's call it "gateway".
Here's my config file:
zuul:
prefix: /api
routes:
api-login:
path: /login/**
url: http://localhost:8070/the-login/
api-other:
...
I want to show the documentation for every service of mine in my gateway application so I created the following:
#Component
#Primary
#EnableAutoConfiguration
public class SwaggerDocumentationController implements SwaggerResourcesProvider{
#Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<SwaggerResource>();
resources.add(swaggerResource("login-service", "/api/login/api-docs", "2.0"));
...
return resources;
}
private SwaggerResource swaggerResource(String name, String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
It works pretty much fine: each time the user goes to /gateway/api/login it gets redirected to my microservice, so to /the-login/. Also, when the user goes to /gateway/login/swagger-ui.html they can see the documentation.
The problem is that when the user tries an api from the swagger ui documentation page they get:
{
"timestamp": "2018-05-12T15:35:38.840+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/the-login/the-login/LoginTest"
}
As you can see zuul added one more /the-login to the path!
Is this a swagger-ui bug or am I getting something wrong?
You are using the-login as context path in api-login service. So when you load gateway service swagger, its add the-login into swagger base url as well, like this.
Thats the reason the-login is getting appended twice (one from base-url and one from config). This might be a bug in swagger ui.
A workaround I tried and it worked
Remove /the-login from application.yml
zuul:
prefix: /api
routes:
api-login:
path: /login/**
url: http://localhost:8070/
api-other:
Now swagger resource will not load, so need to modify swagger resource path.
Add the-login in swagger resources
resources.add(swaggerResource("login-service", "/api/login/the-login/api-docs", "2.0"));

Return all available mappings on RequestMethod GET

I'm learning about spring boot and having a REST Server with JPA.
For my RestControllers, I wanted to have the behavior on the base page that when someone goes to the base page they would be able to see all the available RequestMappings under the base page.
#RestController
#RequestMapping("/food")
public class FoodRestController {
#RequestMapping(value = "/all", method = RequestMethod.GET)
#ResponseBody
public Iterable<Food> printAllFoods() {
return foodRepository.findAll();
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ResponseEntity<?> addFood(#RequestBody Food f) {
foodRepository.save(f);
HttpHeaders httpHeaders = new HttpHeaders();
return new ResponseEntity<Food>(f, httpHeaders, HttpStatus.CREATED);
}
So for the above going to "localhost:8080/food" would give a page showing something like not a valid endpoint, possible endpoints are localhost:8080/food/all or localhost:8080/food/add.
I could just have a RequestMapping with GET and return it as a body but it would be a manually typed response. Wanted to see if Spring offers anything like this
You can also use Swagger . It is actually a documentation framework. It also builds around a beautiful UI to try out the APIs that are available along with the documentation.
The SpringBoot Actuator already has a functionality that does something like this. Add a dependency on the SpringBoot Actuator, start up your application and point your browser to:
http://[yourHostAndPort]/mappings
This will produce something like the following (assuming JSON), which contains ALL mappings that are a part of your project (Spring endpoints too!).
{
...
"{[/refresh],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke()"
},
"{[/restart],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.cloud.context.restart.RestartMvcEndpoint.invoke()"
},
"{[/configprops],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()"
},
"{[/env],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.cloud.context.environment.EnvironmentManagerMvcEndpoint.value(java.util.Map<java.lang.String, java.lang.String>)"
},
...
}
This snippet shows a small handful of the mappings that are available from some other Actuator endpoints.
I understand that your requirements are a bit different, so if this setup isn't exactly what you need, you should be able to create your own endpoint(s) that do something similar, just by browsing the SpringBoot Actuator source code. The specific file that does the majority of the work for the mappings endpoint is org.springframework.boot.actuate.endpoint.RequestMappingEndpoint.java.

Resources