Multipart File upload using Springfox and Swagger-ui - java

I'm using Spring MVC as a rest controller and I've integrated Swagger-ui with my controller using Springfox. I'd like to have a method that is able to upload a file via the Swagger-ui interface. I only need two parameters, a long acting for an object id and the file to be uploaded.
#RestController
public class controller{
#RequestMapping(value="/upload", method=RequestMethod.POST)
public void uploadFile(#RequestParam Long id,
#RequestParam MultipartFile file){
//do some stuff
}
}
I've tried almost everything and I can't get a file upload button to appear. However, if I do:
#RestController
public class Controller{
#RequestMapping(value="/upload", method=RequestMethod.POST)
public void uploadFile(#RequestParam Long id,
#RequestPart File file){
//do some stuff
}
}
The file upload button appears, but it always throws http code 415 when trying to upload a file. Besides, I need the input to be a MultipartFile, not a regular File. Even if I use the #RequestPart annotation with Multipart File, the choose file to upload button does not appear. How can I get this to work???? Even:
#RestController
public class Controller{
#RequestMapping(value="/upload", method=RequestMethod.POST)
public void uploadFile(#RequestPart String metaData,
#RequestPart MultipartFile file){
//do some stuff
}
}
Won't work. If someone could give a walkthrough of how to get this button to appear for MultipartFile? I'd be forever grateful.

I think you are missing the consumes attribute of the #RequestMapping in your second snippet. See the following example
#RequestMapping(
path = "/upload",
method = RequestMethod.POST,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> handleUpload(
#RequestPart("file") MultipartFile file,
#RequestParam("someId") Long someId,
#RequestParam("someOtherId") Long someOtherId) {
return new ResponseEntity<>();
}

Use
#RequestPart(required = true) MultipartFile file
And use the version number 2.1.0 or latest, there is a bug with previous versions.
https://github.com/springfox/springfox/issues/786

In my situation, there were two things I needed to do
My MultipartFile request param had to be named 'file', otherwise, swagger-ui wouldn't display the file upload input control
#RequestParam("file") MultipartFile file
I had to register the following bean
#Bean(name = "multipartResolver")
public CommonsMultipartResolver commonsMultipartResolver(){
return new CommonsMultipartResolver();
}

Try using #RequestPart for MultipartFile instead of #RequestParam
#RestController
public class controller {
#RequestMapping(value="/upload", method=RequestMethod.POST)
public void uploadFile(#RequestParam Long id,
#RequestPart MultipartFile file) {
//do some stuff
}
}

Two things...
Value of consumes should should be "multipart/form-data". consumes="multipart/form-data"
#RequestPart("file") #ApiParam(value="File", required=true) MultipartFile file

Related

How to receive multipart request in Spring App

I've seen many sources and also few questions on SO but didn't find solution.
I want to send to my Spring app POST/PUT-requests that contain JSON-object Car and attached file.
For the moment I have a CarController which correctly works with JSON-objects
#PutMapping("/{id}/update")
public void updateCar(#PathVariable(value = "id") Long carId, #Validated #RequestBody Car car) throws ResourceNotFoundException {
// I can work with received car
}
I also have a FileController which correctly works with file
#PostMapping("/upload")
public void uploadFiles(#RequestParam("file") MultipartFile file) throws IOException {
// I can work with received file
}
But how should my method look like to be able to work with both car and file? This code doesn't provide me any of car or file.
#PutMapping("/{id}/update")
public void updateCar(#PathVariable(value = "id") Long carId, #Validated #RequestBody Car car, #RequestParam("file") MultipartFile file) throws ResourceNotFoundException, IOException {
// can not work neither with car nor with file
}
Separate controllers work well during test from Postman. But when I try third code I got these results:
You can use consumes = { MediaType.MULTIPART_FORM_DATA_VALUE } field of #RequestMapping annotation and #RequestPart annotation for method parameters:
ResponseEntity<> foo(#RequestPart ParType value, #RequestPart MultipartFile anotherChoice) {
...
Yes, I agree with Vladimir; multipart/form-data, #RequestParts instead of body & param:
#PutMapping(value = "/{id}/update", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public void updateCar(#PathVariable(value = "id") Long carId,
#RequestPart("car") Car car,
#RequestPart("file") MultipartFile file) {
...
Then in Postman:
use Body>form-data
when issues:
display Content-Type column.
set Content-Type per part.
There is nothing wrong with your code and it could work as it is.
You could eventually improve its readability by using #RequestPart instead of #RequestParam and #RequestBody when it's a multipart request.
You can find more details about multipart requests in this article https://www.baeldung.com/sprint-boot-multipart-requests
Most importantly, to make it work/ or to test in the correct way:
When using postman for multipart requests, you have to define the content type of each RequestPart.
It's a hidden column in the form-data screen, that you can show as follows:
Check the box "Content-Type" and the new column will appear:
And finally, define the content type of each part.

File upload problem. In what form to send data to API

Prompt, I have a problem uploading files to the database. I need to implement a controller that accepts a number of files from 1 to n with additional parameters.
For example:
public class FilesDTO{
private String name;
private String type;
private MultipartFile file;
get / get
}
further implement the controller, for example:
#RequestMapping(value = "file/{id}", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<?> addFile(#PathVariable Long id, #ModelAttribute List<FilesDTO> filesRequestList) throws IOException {
// log..
}
how to send a request correctly and in what form to this controller?
If you use postman.
maybe I misunderstood how to implement the controller, tell me how best to do
maybe this should be done not through dto
Use array of multipartfile (you can try to use list instead) and send files by form-data
#RequestMapping(value = "file/{id}", method = RequestMethod.POST)
public ResponseEntity<?> addFile(#PathVariable Long id, #RequestBody MultipartFile[] filesRequestList) throws IOException {
// log..
}

How to design API in Spring MVC?

I have a Spring MVC controller but I'm not sure that it is a good or bad design. As far as I know, api versioning is missing but apart from that I implemented Swagger for documentation and added SpringSecurity and tried to follow YARAS(Yet Another RESTful API Standard) to build it but I need another eye on that to comment it.
#Slf4j
#Controller
#RequestMapping
#RequiredArgsConstructor
public class XGameController implements GameController {
private final GameService gameService;
private final ObjectMapper mapper;
#RequestMapping(value = "/", method= RequestMethod.GET)
public String index() {
return "game";
}
#RequestMapping(value = "/login", method= RequestMethod.GET)
public String login() {
return "login";
}
#Secured("ROLE_USER")
#RequestMapping(value = "/games", method= RequestMethod.POST)
public String initializeGame(Model model) {
log.info("New XGame is initializing...");
Game game = new Game();
game = gameService.initializeGame(game.getId());
try {
model.addAttribute("game", mapper.writeValueAsString(game));
} catch (JsonProcessingException e) {
log.error(e.getMessage());
}
log.info("New XGame is initialized successfully!");
return "game";
}
#Secured("ROLE_USER")
#RequestMapping(value = "/games/{gameId}", method= RequestMethod.PUT)
public #ResponseBody Game play(#PathVariable("gameId") String gameId,
#RequestParam Integer pitNumber,
#RequestParam String action) {
log.info("Sowing stone is triggered...");
return gameService.executeGameRules(UUID.fromString(gameId), pitNumber);
}
#RequestMapping(value = "/403", method= RequestMethod.GET)
public String error403() {
return "/error/403";
}
}
My swagger snapshot;
I would make some changes.
In /games/{gameId} I would use PATCH instead of PUT. The reason is that PUT is intended to completely replace the resource (in your case, the Game). This does not seem to be what you are doing in this endpoint. PATCH is intended to partially update a resource, which seems much more suited to what you are doing here.
Still in /games/{gameId} I would use the request body to provide the needed data instead of query parameters. It simply doesn't seem right. Query parameters are way more suited to GET requests than to POST, PUT or PATCH.
I would rename /403 to something else that actually gives some context about what 403 is. Having said this, I would go with /error-pages/403. Additionally, I would also consider removing this endpoint from the swagger specification.
Other than this, it seems fine to me.
Some advices :
Use a path that represents the context or the idea of your controller and you can add the version
#RequestMapping("/V1/xgame")
Use specialized annotations such as : #GetMapping, #PostMapping etc...
For /403 use a meaning full name such as /errors
Use custom message that you will return to the users. For that you need a ControllerAdvice.
Google on patterns and best practices Rest API design
Read some books for better undertanding.
Firstly, instead of #RequestMapping use a specific Mapping(Get, Post,etc.) and the use of type of mapping is up to you which you find more particular to the cause of using it
if you are redirecting from a page to homepage try to use return "redirect:"/url"" instead of just returning HTML file directly.
Rename your method for error, RequestMapping value to some more reasonable name.
instead of using return "/error/403"
use return "redirect:/error/403"

Spring MVC: Not able to connect to Rest service

Have written a spring controller Get method and trying to call that with postman but getting error
Could not get any response
There was an error connecting to https://localhost:8081/MyPortal/shared?video0=14&video1=15.
Even when I try to debug, the call is not going to the controller method. Not sure what is happening.
Here is the controller code:
#Controller
public class SharedLinkController {
#Autowired
VideoService videoService;
#RequestMapping("/shared")
public ModelAndView getSharedVideo(HttpServletRequest request, HttpServletResponse response,
#RequestParam(value="video0", required=false) Long video0,
#RequestParam(value="video1", required=false) Long video1,
#RequestParam(value="video2", required=false) Long video2,
#RequestParam(value="video3", required=false) Long video3){
List<Lab> videos = new ArrayList<Lab>();
// some processing with video0, video1 etc....
ModelAndView model = new ModelAndView("index");
model.addObject("videos", videos);
return model;
}
}
And this is how am trying to call the API:
https://localhost:8081/MyPortal/shared?video0=14&video1=15
Am not trying to make it a RestController as I need to redirect the call to a webpage. Is there anything wrong with the code?
Please suggest.
Do you have a class that scans basepackages and registers your controller as a bean, like the following?
#SpringBootApplication(scanBasePackages= "se.yourpackage.src")
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
Nevermind the RestController, but you need to add the requestmapping
#RequestMapping("/MyPortal")
public class SharedLinkController {
You might also need to add the path of the view files in your file called
application.properties
for example:
spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp
I suggest to check your url and your view name.
You try to connect url : connecting to http://localhost:8081/yourPortal/shared?video0=14&video1=15
#Controller
public class SharedLinkController {
#Autowired
VideoService videoService;
#RequestMapping("/shared")
public ModelAndView getSharedVideo(ModelAndView mav,
#RequestParam(value="video0", required=false) Long video0,
#RequestParam(value="video1", required=false) Long video1,
#RequestParam(value="video2", required=false) Long video2,
#RequestParam(value="video3", required=false) Long video3){
List<Lab> videos = new ArrayList<Lab>();
// ...
mav.addObject("videos", videos);
mav.setViewName("index");
return mav;
}
}
You need to set the context to youe spring boot application,
try setting
server.context-path=/MyPortal
This way you can define the root context to your application as
http:localhost:8081/MyPortal/

Spring controller gets invoked but returns 404

I am writing a Spring Boot application. I have written a simple controller that gets invoked whenever the endpoint is hit, but it still returns status 404 and not the specified return value.
HelloController
#Controller
public class MessageRequestController {
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
public String hello() {
System.out.println("Hit me!");
return "Hello, you!";
}
}
Now whenever I call localhost:8080/hello, I see the console log "Hit me!", but "Hello, you!" is never returned. Postman outputs:
{
"timestamp": 1516953147719,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/hello"
}
Application.java
#SpringBootApplication
#ComponentScan({"com.sergialmar.wschat"}) // this is the root package of everything
#EntityScan("com.sergialmar.wschat")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Change your method return a ResponseEntity<T>
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
public ResponseEntity<String> hello() {
System.out.println("Hit me!");
return new ResponseEntity<String>("Hello, you!", HttpStatus.OK);
}
or change the controller to RestController
#RestController
public class MessageRequestController {...}
CURL
ubuntu:~$ curl -X GET localhost:8080/hello
Hello, you!
Short version:
Annotate your endpoint method with ResponseBody to bind the return value to the response body.
#Controller
public class MessageRequestController {
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
#ResponseBody
public String hello() {
System.out.println("Hit me!");
return "Hello, you!";
}
}
You can instead annotate your class with RestController instead of Controller to apply ResponseBody to each method of the class.
#RestController
public class MessageRequestController {
#RequestMapping(method = RequestMethod.GET, value = "/hello", produces = "application/json")
public String hello() {
System.out.println("Hit me!");
return "Hello, you!";
}
}
With #Controller, you use the default model-view from Spring Web MVC, and you're actually telling spring to render the view called Hello, you!.tml from your resources directory (src/main/resources/templates for a Spring Boot project, if I remember correctly).
You can read this article for more information about the Spring MVC REST Workflow.
Once you're more familiar with those concepts, you can even further customize your endpoint method using ResponseEntity.
As you see the "hit me", there's no mapping issue, but in your #RequestMapping annotation you specifie a produce type to "application/json" and you return a simple poor String not formatted and without any header('Content-Type: application/json').
Add the header and format the outpout.
When everything seems ok but receive 404, check this answer:
As you know:
In the Spring MVC you can return view as a String or ModelAndView object.
IMPORTANT NOTE:
In both cases you have to pay attention to relative/absolute path:
If you declare / in the beginning of the view name, you are using absolute path.
Namely it does not matter class level #RequestMapping and directly introduce itself as the final view name.
If you do not declare / in the beginning of the view name, you are using relative path (relative to the class path) and therefore it appends to the class level #RequestMapping to construct final view name.
So, you have to consider the above notes when use the Spring MVC.
Example:
1. create two HTML file test1.html and test2.html in the static folder of spring (boot) structure:
Please note that the class level #RequestMapping behaves as a folder path in the case of relative path.
--- resources
--- static
--- classLevelPath //behaves as a folder when we use relative path scenario in view names
--- test2.html //this will be used for relative path [case (2)]
--- test1.html //this will be used for absolute path [case (1)]
create a controller class like as the below. This example shows different cases with return String and ModelAndView in both relative and absolute path.
#Controller
#RequestMapping("/classLevelPath")
public class TestController {
//case(1)
#RequestMapping("/methodLevelAbsolutePath1")
public String absolutePath1(Model model){
//model.addAttribute();
//...
return "/test1.html";
}
//case(1)
#RequestMapping("/methodLevelAbsolutePath2")
public ModelAndView absolutePath2(Model model){
ModelAndView modelAndView = new ModelAndView("/test1.html");
//modelAndView.addObject()
//....
return modelAndView;
}
//case(2)
#RequestMapping("/methodLevelRelativePath1")
public String relativePath1(Model model){
//model.addAttribute();
//...
return "test2.html";
}
//case(2)
#RequestMapping("/methodLevelRelativePath2")
public ModelAndView relativePath2(Model model){
ModelAndView modelAndView = new ModelAndView("test2.html");
//modelAndView.addObject()
//....
return modelAndView;
}
}
Note:
You can specify the suffix of your view files by a ViewResolver (for example InternalResourceViewResolver or spring.mvc.view.suffix=.html in the appliction.properties file of Spring Boot and do not declare .html suffix in the above code.
Best Regard

Categories

Resources