In a spring boot app with kotlin using the controller
// #PostMapping("/:csv", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) // variant 1
#PostMapping("/:csv") // variant 2
fun processKontoCsvFile(
#RequestPart("revenues") #NotNull fileWithRevenues: MultipartFile,
#RequestParam("bank") #NotNull bank: Bank
): ResponseEntity<String> {
return ResponseEntity.ok().body(bank.name + " " + fileWithRevenues.name);
}
the openapi is not generated correctly. Either the multipart file cannot be used as a selct (variant 2 above) or the enumeration value for variable bank (variant 1). See attached pictures.
Dependencies used, no extra configuration:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-data-rest</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-kotlin</artifactId>
<version>1.6.11</version>
</dependency>
What is needed to make that work?
Update:
It works with a PathVariable like so. But still don't know why not with a RequestParam
#PostMapping("/{bank}/:csv", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
fun processKontoCsvFile(
#RequestPart("revenues") #NotNull fileWithRevenues: MultipartFile,
#PathVariable("bank") #NotNull bank: Bank
): ResponseEntity<String> {
return ResponseEntity.ok().body(bank.name + " " + fileWithRevenues.name);
}
Related
I'm building microservices project using spring boot 3 and I have found some problems from api gateway.
when I type each service port I get swagger ui documentation but when I try access it's documentation from api the gateway I get this error "Failed to load remote configuration."
here is my springdoc config for
api-gatway :
<!-- Swagger documentation Dependencies -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-common</artifactId>
<version>3.0.0</version>
<scope>compile</scope>
</dependency>
gateway config:
#Configuration
#Primary
public class SwaggerConfig {
public static final String API_URI = "/v3/api-docs";
private final RouteDefinitionLocator routeLocator;
public SwaggerConfig(RouteDefinitionLocator routeLocator) {
this.routeLocator = routeLocator;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("1.0");
return swaggerResource;
}
}
gateway routing :
#Bean
DiscoveryClientRouteDefinitionLocator dynamicRoutes(ReactiveDiscoveryClient rdc, DiscoveryLocatorProperties dlp){
return new DiscoveryClientRouteDefinitionLocator(rdc,dlp);
}
backend services :
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.9</version>
</dependency>
expecting : enter image description here
but what i get
enter image description here
enter image description here
Using:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.11</version>
</dependency>
In controller I have this request param setup:
#RequestParam(value = "end_time", required = false) #DateTimeFormat(iso = DateTimeFormat.ISO.TIME) LocalTime endTime)
In swagger it is required string($date-time), how to make it string($time)?
For date I use #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) and it is working right.
I am using following spring boot dependencies.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
I configured Authorization header for the rest endpoint like below.
#RestController
#Api(tags = "Welcome Controller", description = "Welcome API")
public class HomeController {
#ApiOperation(value = "Sent secret message", notes = "Sent secret message")
#ApiImplicitParams({
#ApiImplicitParam(name = "Authorization", value = "Access Token", required = false, allowEmptyValue = false, paramType = "header", dataTypeClass = String.class, example = "Bearer access_token") })
#GetMapping("/secret-message")
public Object tokenResponse() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return principal;
}
}
But when I opened the swagger and try to execute the rest end point with Bearer token, this Authorizaiton header is not sent as part of the request. How can we resolve this?
References
https://github.com/tiangolo/fastapi/issues/1037
https://github.com/tiangolo/fastapi/issues/612
This problem is with the latest version of swagger. But things are working fine with swagger 2.9.2
<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>
I'm trying to discover some way to document an API that returns a PDF (or any other file) to download.
Using Spring, my Rest resource is like this:
#Override
#GetMapping(produces = "application/pdf")
public ResponseEntity<InputStreamResource> find(
#PathVariable long id
) {
Result result = service.find(id);
HttpHeaders headers = disableCache();
return ResponseEntity
.ok()
.headers(headers)
.contentLength(result.getSize())
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new InputStreamResource(result.getFileInputStream()));
}
This works very well to download the file. But I don't know the good practice to document the response using Swagger.
Actually, I tried that with Swagger annotations:
#ApiOperation(value = "Some description")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success.")
})
#ResponseStatus(HttpStatus.OK)
#GetMapping(produces = "application/pdf")
ResponseEntity<InputStreamResource> find(
#PathVariable long id
);
But the Swagger returns the content of InputStreamResource as Json on Swagger-ui, what is not the result.
How represent a file download on the response for Swagger?
Let try to update your code like this:
Update headers:
headers.add("Content-Type", "application/pdf");
headers.add("Content-Disposition", "attachment; filename=report.pdf");
Use this annotation:
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success.", response = byte.class)
})
Update the pom:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
Hope it helps.
The annotations were ignored, but this worked for me:
TypeResolver typeResolver = new TypeResolver();
new Docket(DocumentationType.OAS_30)
.alternateTypeRules(AlternateTypeRules.newRule(
typeResolver.resolve(ResponseEntity.class, InputStreamResource.class),
typeResolver.resolve(Byte.class),
DIRECT_SUBSTITUTION_RULE_ORDER));
I have a Standalone Jersey server running at the beginning of my JunitTest. I'm testing if my JaxRS controller works, as well as my custom HttpClient. Please note that I've always been able to use this JaxRsResourceController embedded in glassfish.
Here is the JaxRsController (light version)
#Path("root")
public class JaxRsResourceController implements
ResourceController<HttpServletRequest> {
#Context
private UriInfo context;
#Context
HttpServletRequest request;
#Context
HttpServletResponse response;
#GET
public String hello(){
System.out.println("Uri is "+this.context.getBaseUri().toString());
return "Hello "+peoples;
}
}
I have no problem with the client, but when I start the server, I have :
GRAVE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for field: javax.servlet.http.HttpServletRequest com.robustaweb.library.rest.controller.implementation.JaxRsResourceController.request
SEVERE: Missing dependency for field: javax.servlet.http.HttpServletResponse com.robustaweb.library.rest.controller.implementation.JaxRsResourceController.response
at com.sun.jersey.api.container.httpserver.HttpServerFactory.create(HttpServerFactory.java:172)
at com.robustaweb.library.rest.server.JerseyServer.startServer(JerseyServer.java:44)
Basically it says that at the #Context injection time, there is no dependency on the HttpServletRequest.
However if I remove the #Context annotations on request and response, but keep it for UriInfo context, it's ok, and I can read the Uri.
I changed a few times the Maven pom wich is now to force the libs in:
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
Any idea ?
servlet dependencies were separated to another module, try adding
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.14</version>
</dependency>
to your pom.
It was not easy, but I found out. The thing is that in my JUnit test, I was creating the server like this :
HttpServer server = HttpServerFactory.create(url);
But that way, you create a lightweight container that does not have servlet containers, and so is the failure reason. So in order to have it all, I used the jersey-test-framework that allow to use the Grizzly web container (or even Embedded glassfish).
Here is the maven :
<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<!-- Unit test are using jersey server directly -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey.test.framework</groupId>
<artifactId>jersey-test-framework</artifactId>
<version>1.0.3</version>
<scope>test</scope>
</dependency>
</dependencies>
Here is the JerseyServerTest : note that it extends JerseyTest
public class JerseyServerTest extends JerseyTest {
protected String baseUri = "http://localhost:" + TestConstants.JERSEY_HTTP_PORT + "/";
public JerseyServerTest() throws Exception {
super("com.robustaweb.library.rest.controller");
/*
It's possible to NOT call the super() but to manually do :
1) ApplicationDescriptor appDescriptor = new ApplicationDescriptor()
.setRootResourcePackageName(resourcePackageName) // resource packages
.setContextPath(contextPath) //context of app
.setServletPath(servletPath); // context of spi servlet
2)setupTestEnvironment(appDescriptor);
*/
}
#Test
public void testHelloWorldRequest() {
SunRestClient client = new SunRestClient(baseUri + "root");
String result = client.GET("", null);
System.out.println(result);
}
#Test
public void testDeleteRequest() {
SunRestClient client = new SunRestClient(baseUri + "root");
String result = client.DELETE("john", null);
System.out.println(result);
}
}
And finally the Resource file, that contains #GET and #DELETE
#Path("root")
public class JaxRsController extends JaxRsResourceController{
List<String> peoples = new ArrayList<String>();
#GET
public String hello(){
System.out.println("Uri is "+getUri());
return "Hello "+peoples;
}
#DELETE
#Path("{name}")
public String deletePeople(#PathParam("name") String name){
System.out.println("deleting "+name);
this.peoples.remove(name);
return String.valueOf(peoples.size());
}
}
And now it works !
I had some help in this article, and there is a small chapter on the documentation. Beeing able to attach the source code of the Jersey framework really helped, so thantks to IntelliJ also.