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

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..
}

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.

How to bind other form data arguments to Map in Class?

I have method in Spring RestController:
#RequestMapping(value = "/mapping", method = { RequestMethod.POST }, consumes = "multipart/form-data")
public ResponseEntity fileQuery(MyRequest request) {}
And a class, MyRequest:
public class MyRequest{
private String url;
private String method;
private String body;
private MultipartFile file;
// I have tried with MultiValueMap and no result
private Map<String,String> formData;
//getters and setters here
}
I want to create a method that accepts multipart / form-data requests with the following known arguments:
URL,
Method,
Body,
File,
Other String:String type arguments.
My solution is not working. Map is null in this case. How can I achieve something like this?
For better understanding I attached a screenshot from postman:

liferay spring portal #ResourceMapping can't trigger different types of http methods

I have created a liferay portlet application using Spring, thymeleaf and AngularJS. For communication between AngularJS and spring I need to create some rest calls which I have created using #ResourceMapping like as shown below. The application is working fine but the problem is that I don't know how to make GET, DELETE, PUT http REST calls since #ResourceMapping is not allowing to specify any methods.
#ResourceMapping(value="getUserDetail")
public void userDetail(#RequestParam long userId, ResourceResponse response) throws Exception {
Users users = new Users(userId);
// some logic
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
JSON_MAPPER.writeValue(response.getPortletOutputStream(), users);
}
When I used #RequestMapping instead of #ResourceMapping like as shown below
#RequestMapping(value="getUserDetail", method=RequestMethod.GET)
#ResponseBody
public void userDetail(#RequestParam long userId, ResourceResponse response) throws Exception {
System.out.println("Got detail request for user with id {} "+ userId);
// UserDetail userDetail = this.userService.getPortalUserDetail(userId);
List<String> users = new ArrayList<String>();
users.add("Manu");
users.add("Lissie");
users.add("John");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
JSON_MAPPER.writeValue(response.getPortletOutputStream(), users);
}
I have got
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping': Initialization of bean failed; nested exception is java.lang.IllegalStateException: Mode mappings conflict between method and type level: [getUserDetail] versus [view]
Can anyone please tell me some solution for this
How to create different types of http calls using #ResourceMapping
Can we use #RequestMapping instead of #ResourceMapping in Liferay Spring portlet for REST calls
How can we create resource based REST urls like getUser/12/mumbai
How can we send REST json as body instead of Request Param
Mode mappings conflict exception
The question doesn't show it, but your controller probably has #RequestMapping("view") annotation. This type level mapping is in conflict with the method level mappings. You should remove #RequestMapping annotation on the controller class.
Request mapping examples
#Controller
public class SampleRESTFullController {
// Simple GET
#RequestMapping(value = "/helloSample", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public #ResponseBody List<HelloSample> helloSample() { ... }
// GET with path variable
#RequestMapping(value = "/helloSample/sampleId/{sampleId}", method = RequestMethod.GET)
public #ResponseBody HelloSample helloSample(#PathVariable("sampleId") Long sampleId) { ... }
// POST with #RequestBody
#RequestMapping(value = "/helloSample", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public #ResponseBody HelloSample createSample(#RequestBody HelloSample helloSample) { ... }
// PUT with path variable and #RequestBody
#RequestMapping(value = "/helloSample/sampleId/{sampleId}", method = RequestMethod.PUT)
#ResponseStatus(HttpStatus.NO_CONTENT)
void update(#PathVariable("sampleId") long sampleId, #RequestBody HelloSample helloSample) { ... }
// DELETE
#RequestMapping(value = "/helloSample/sampleId/{sampleId}", method = RequestMethod.DELETE)
#ResponseStatus(HttpStatus.NO_CONTENT)
void delete(#PathVariable("sampleId") long sampleId) { ... }
}
I took the examples from Using RESTFul services with Liferay blog post. It answers all your questions and presents tons of examples. Pay attention to Spring configuration, which makes the RESTful services possible (especially the view resolver and message converter).
1. How to create different types of http calls using #ResourceMapping
If you want to a REST Api with Complete Actions (GET, POST, PUT, DELETE) you need to use #RequestMapping.
2. Can we use #RequestMapping instead of #ResourceMapping in Liferay Spring portlet for REST calls
You should be able to use.
3. How can we create resource based REST urls like getUser/12/mumbai
#RequestMapping(value="getUser/{userId}/mumbai", method=RequestMethod.GET)
#ResponseBody
public List<String> userDetail(#RequestParam("userId") long userId) throws Exception {
System.out.println("Got detail request for user with id {} "+ userId);
//UserDetail userDetail = this.userService.getPortalUserDetail(userId);
List<String> users = new ArrayList<String>();
users.add("Manu");
users.add("Lissie");
users.add("John");
return users;
}
4. How can we send REST json as body instead of Request Param
You can use #RequestBody
#RequestMapping(value="saveUser/{userId}", method=RequestMethod.GET)
#ResponseStatus(HttpStatus.CREATED)
public void userDetail(#RequestParam("userId") long userId, #RequestBody User user) throws Exception {
// Logic
}
How to create different types of http calls using #ResourceMapping
Here are some examples that may help you, that's how i use #RequestMapping:
// GET
#RequestMapping(value = "/api/something", method = RequestMethod.GET)
#ResponseBody
public boolean getSomething() {
return "something";
}
// GET with param
#RequestMapping(value = "/api/something/{id}", method = RequestMethod.GET)
#ResponseBody
public boolean getSomething(#PathVariable("id") Long id) {
return id;
}
Instead of RequestMethod.GET you can use RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE and so on...
How can we send REST json as body instead of Request Param
Here is a code snippet that i currently use with an AngularJS FrontEnd for user registration. It works just fine and i use #RequestMapping:
#ResponseBody
#RequestMapping(value = "/auth/register", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> register(#RequestBody User user) {
user = userService.initUser(user);
Authentication authentication = securityUserDetailsService.register(user);
if (authentication != null) {
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
User authUser = securityUserDetailsService.getAuthenticatedUser();
return new ResponseEntity<>(authUser, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
In order to consume JSON you do:
RequestMapping(value = "/whatever", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
In order to produce (return) JSON you do:
RequestMapping(value = "/whatever", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
Also since you use Spring i think you should take a look at Spring Data and Spring Data Rest. This way you can expose your business models as RESTful endpoints.
How can we create resource based REST urls like getUser/12/mumbai
So in order to expose this endpoint getUser/12/mumbai that's what you should do:
// mumbai hardcoded
#RequestMapping(value = "/getUser/{id}/mumbai", method = RequestMethod.GET)
#ResponseBody
public User getUser(#PathVariable("id") Long id) {
// go get the user ...
return user;
}
// mumbai as a param
#RequestMapping(value = "/getUser/{id}/{prop}", method = RequestMethod.GET)
#ResponseBody
public User getUser(#PathVariable("id") Long id, #PathVariable("prop") String prop) {
// go get the user ...
return user;
}
Lastly can you please try to change
public void userDetail (...
to this
public ResponseEntity<userDetail > (...
There are following methods to use rest app with angular js
#RequestMapping(value = "/saveuser", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#RequestMapping(value = "/getemployee", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#RequestMapping(value = "/editCountry", method = RequestMethod.PUT)
#RequestMapping(value = "/deleteCountry", method = RequestMethod.DELETE)
and use following javascript to communicate with spring controller
var formData = {
"userName" : 'Vasim',
"password" : '123456',
"roleName" : 'Admin'
};
var response = $http.post('add', formData);
response.success(function(data, status, headers, config) {
$scope.message = data;
});
var formData = {
"userName" : 'Vasim',
"password" : '123456',
"roleName" : 'Admin'
};
var response = $http.put('edit', formData);
response.success(function(data, status, headers, config) {
$scope.message = data;
});
$scope.delete= function(employeeId) {
$http['delete']('delete', {
params : {
'employeeId' : employeeId
}
}).
success(function(data) {
$scope.msg = data;
});
$http.get('get',{params:{
'id':id
}
}).success(function(data) {
$scope.employees = data;

Multipart File upload using Springfox and Swagger-ui

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

How to read flash attributes after redirection in Spring MVC 3.1?

I would like to know how to read a flash attributes after redirection in Spring MVC 3.1.
I have the following code:
#Controller
#RequestMapping("/foo")
public class FooController {
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(...) {
// I want to see my flash attributes here!
}
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public ModelAndView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new ModelAndView().setViewName("redirect:/foo/bar");
}
}
What I am missing?
Use Model, it should have flash attributes prepopulated:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(Model model) {
String some = (String) model.asMap().get("some");
// do the job
}
or, alternatively, you can use RequestContextUtils#getInputFlashMap:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(HttpServletRequest request) {
Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
if (inputFlashMap != null) {
String some = (String) inputFlashMap.get("some");
// do the job
}
}
P.S. You can do return return new ModelAndView("redirect:/foo/bar"); in handlePost.
EDIT:
JavaDoc says:
A RedirectAttributes model is empty when the method is called and is
never used unless the method returns a redirect view name or a
RedirectView.
It doesn't mention ModelAndView, so maybe change handlePost to return "redirect:/foo/bar" string or RedirectView:
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public RedirectView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new RedirectView("/foo/bar", true);
}
I use RedirectAttributes in my code with RedirectView and model.asMap() method and it works OK.
Try this:
#Controller
public class FooController
{
#RequestMapping(value = "/foo")
public String handleFoo(RedirectAttributes redirectAttrs)
{
redirectAttrs.addFlashAttribute("some", "thing");
return "redirect:/bar";
}
#RequestMapping(value = "/bar")
public void handleBar(#ModelAttribute("some") String some)
{
System.out.println("some=" + some);
}
}
works in Spring MVC 3.2.2
For all those like me who were having problems with seeing the POST url in the browser when a validation would fail.
The POST url is a private url that should not be exposed to users but it was automatically rendered when a validation failed. i.e. if a field was below a minimum length. I was using #Valid. I wanted the original GET url of the form to show at all times even when validation bounced back to the form, so I did the following:
if (validation.hasErrors()) {
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.story", validation);
redirectAttributes.addFlashAttribute("story", story);
return new ModelAndView("redirect:/january/2015");
where story is the form object representation, redirectAttributes are RedirectAttributes you put in the method signature and validation is the BindingResult. /january/2015 is the mapping to the GET controller where the form lives.
After this implementation, in the mapping for /january/2015, story comes in intact as follows:
Story story= (Story) model.asMap().get("story");
//story from the POST method
I had to augment my GET method and check if this was not null. If not null, then send this to the form else I would send a newly initialized Story type to the form as default behaviour before.
In this manner, I am able to return to the form with the bindingresults intact (errors show on form) but have my GET url in place of the post url.

Categories

Resources