How to POST MultipartFile with additional information in one POST request in Spring Boot and Swagger? - spring-boot

I have the following controller structure:
#PostMapping(
value = "/whatever",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<ResponseDTO> method(#RequestBody RequestDTO dto, #RequestPart MultipartFile file) {
...
}
Spring Boot version: 2.3.4.RELEASE
I am using swagger:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
Tried and failed:
With specifying the parameters as #RequestBody and #RequestPart, when using the two together I am getting error 415.
If I remove any of the inputs, the request works, so I wither upload file or DTO.
If I am using #RequestPart for both of them, Swagger does not display the DTO json input field.
If the MultipartFile is not annotated with #RequestPart, the upload button does not appear.
If both of the parameters are annotated with #RequestParam, the upload fails with error 415
Question:
How can I upload a file with additional json information via Swagger?

Related

CommonsMultipartFile cannot be resolved to a type

After upgrading spring from older version, to Spring 6.0.4
I noticed this file has moved:
from here (Spring docs 4.3.x for CommonsMultipartFile
web on 6.0.4 doesn't have commons anymore like 4.3.x did
where did this file move?
I tried including this in my pom, didn't help
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
UPDATE
reducing the version of spring-web from 6.0.0 to 5.3.25 resolves the issue
BUT 5.3.25 has vulnerabilities
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<!-- version>${spring.version}</version !-->
<!-- some issue CommonsMultipartFile is missing starting with v 6.0.0 !-->
<version> version>
</dependency>
As per https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x in Spring 6
Several outdated Servlet-based integrations have been dropped: e.g. Apache Commons FileUpload (org.springframework.web.multipart.commons.CommonsMultipartResolver)
...
We recommend org.springframework.web.multipart.support.StandardServletMultipartResolver for multipart file uploads
Obviously CommonsMultipartFile is also gone.
You can remove the commons-fileupload dependency from your project and in your controllers you can use MultipartFile that has almost the same methods as CommonsMultipartFile did, or MultipartHttpServletRequest:
#RestController
class MyController {
#PostMapping(path = "/upload)
public ResponseEntity<SomeResult> upload(... , #RequestPart MultipartFile file1) {
...
}
#PostMapping(path = "/upload2")
public ResponseEntity<SomeResult> upload2(MultipartHttpServletRequest request,
...) {
MultipartFile multipartFile = request.getFile("file1");
InputStream inputStream = multipartFile.getInputStream();
...
}
}

Spring boot - springdoc custom url for swagger-ui

I need to use a specific url for Swagger-ui. I have tried to use the property "springdoc.swagger-ui.path" but it only redirects.
application.propperties:
springdoc.swagger-ui.path=/helloWorld/swagger
Url in browser: http://localhost:8181/helloWorld/swagger
but the url changes to the following when hits enter button:
http://localhost:8181/manager/swagger-ui/index.html?configUrl=/manager/swagger/swagger.json/swagger-config
the question is, how can i make the path be only http://localhost:8181/helloWorld/swagger/index.html or http://localhost:8181/helloWorld/swagger once i've hit enter (I need the word "swagger-ui" and configUrl disappear)?
I`m uisng Springdoc and even tried with springfox
Pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.2</version>
</dependency>
application.propperties:
springdoc.api-docs.path=/helloWorld/swagger/swagger.json
springdoc.swagger-ui.path=/helloWorld/swagger
I guess it's version dependent regarding the answer.
if you're using 2.8.0 and above, you could use following dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
And create a controller which will just redirect to the main swagger page, like this:
#Controller
public class SwaggerController {
#RequestMapping("/")
public String index() {
return "redirect:swagger-ui.html";
}
}
But if you use 2.6.1 or like that you can personalize your swagger configuration java class extending WebMvcConfigurerAdapter and overwriting the addViewController, like this:
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController(MAIN_URL, SWAGGER_UI_URL).setContextRelative(true);
registry.addRedirectViewController(MAIN_URL + "/", SWAGGER_UI_URL).setContextRelative(true);
}
That's it. Both approaches used the same dependencies but with different versions.

Spring RequestParam validation in Kotlin is not working

I am trying to validate #RequestParam in Kotlin, however it does not work. Currently I am using Kotlin 1.4.20, Spring boot 2.3.5, and java 1.8.
Here is my controller:
#Validated
#RestController
#RequestMapping("api/v1/")
class myController{
#GetMapping("/age", produces = [MediaType.APPLICATION_JSON_VALUE])
fun findArticlesByAge(#RequestParam #Valid #Min(6) age: Int): ResponseEntity<Article> =
ResponseEntity
.ok()
.body(Article())
}
Hibernate validator is already in the effective pom:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.6.Final</version>
</dependency>
Request:
http://localhost:8080/api/v1/age?age=2
Response:
200
That is the simple validation which is not working, however I want to do more complex validations through custom annotations and ConstraintValidator. If I make it work with the simple case #Min(6) probably it will start working also with the custom annotation.
So there was a dependency missing:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
I added this and the validation started working. However, it throws 500 internal server error with the validation message, which can be fixed by a ControllerAdvice.

Spring Data REST Endpoints Not Generating In Swagger UI

I've implemented a controller using #BasePathAwareController which also takes advantage of Spring Data REST (for finds to expose sort/size etc.)
along with some custom endpoints (for updates etc.). The application is working as expected and the endpoints Spring
Data REST generates are working as expected and I can see the self links appear in the responses, however, i can't see
these endpoints in Swagger UI. I can just see my custom endpoints
defined in my Controller.
According to this post I need to use Swagger 3.0.0-SNAPSHOT with #EnableSwagger2WebMvc
Here's my configurations:
My app.yml:
spring:
data:
rest:
basePath: /api/v1
My POM file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/>
</parent>
<properties>
<springfox.swagger.version>3.0.0-SNAPSHOT</springfox.swagger.version>
</properties>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-data-rest</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
Swagger Config File:
#Configuration
#Import(SpringDataRestConfiguration.class)
#EnableSwagger2WebMvc
public class SwaggerConfig {
#Bean
public Docket api(ServletContext servletContext) {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.package.name"))
.paths(PathSelectors.any())
.build().apiInfo(apiEndPointsInfo());
}
private ApiInfo apiEndPointsInfo() {
return new ApiInfoBuilder().title("My App REST API's")
.description("My App REST API's")
.version("1.0").build();
}
}
My repo:
#RepositoryRestResource(exported=true, collectionResourceRel="group", path="group")
public interface GroupRepository extends JpaRepository<Group, Long>, JpaSpecificationExecutor<Group> {
}
Why can't i see the default endpoints that Spring Data REST produces?
I found the issue to my problem. I wasn't aware that Spring Data REST doesn't expose the generated endpoints under the controller package name I had specified here:
.apis(RequestHandlerSelectors.basePackage("my.package.name"))
so when I changed the above line to:
.apis(RequestHandlerSelectors.any())
and I could see the JPA respository endpoints.

No value at JSON path "$"

I am testing rest controller. Here is test code:
mockMvc.perform(get("/index/get-all"))
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$",hasSize(2)));
I get response body:
Body = [{"id":"123"},{"id":"1234"}]
And I get error:
java.lang.AssertionError: No value at JSON path "$", exception: net/minidev/json/writer/JsonReaderI
What am I doing wrong?
Your body returns an array with objects. To access each object in Spring MVC Test use following assertion:
.andExpect(jsonPath("[0].id").value("123"))
.andExpect(jsonPath("[1].id").value("1234"))
Get the same error.
Try to use higher version json-smart in your dependency, and exclude the lower version from the package which contains json-smart. For me, I changed my dependency to:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>json-smart</artifactId>
<groupId>net.minidev</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
Double check your url: "/index/get-all" and make sure you are using the full path of your controller class. I ran into this same issue because my url was missing a part of the path ("/Folder/"). What I had been using was "/folderId=1234" and realized I forgot to add the controller's #RequestMapping annotated part at the top of the class, so it was supposed to be "/Folder/folderId=1234".
Example controller code
#RestController
#RequestMapping ("/Folder")
public class FolderController {
#RequestMapping (value = "/folderId={Id}", method = RequestMethod.GET)
public Folder getFolderById (#PathVariable String folderId, HttpSession session)
{
// controller code
}
}
The incorrect url I was using.
mockMvc.perform(
get("/folderId=1234")
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$",hasSize(2)));
Correct url.
mockMvc.perform(
get("/Folder/folderId=1234")
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$",hasSize(2)));
I had a scenario where the stack trace was indicating something similar to your error:
java.lang.AssertionError: No value at JSON path "$[0].contentId"
Caused by: java.lang.NoClassDefFoundError: net/minidev/json/writer/JsonReaderI
I found this post: https://github.com/json-path/JsonPath/issues/159#issuecomment-411306322 which caught my attention. I was using the net.minidev:json-smart:1.1.1 in my project.
What I did was just updating the version to 2.3 and the issue was solved.

Resources